[gamin] massive patch



Yo,

So, over the last few days I have completed my big changes to gamin wrt
to the poll stuff. The test suite passes for both dnotify & inotify with
these changes. I'd like to get them in ASAP, so I can stop carrying all
this baggage.

It's pretty much what I discussed earlier:

- Separate generic poll stuff from dnotify specific poll stuff
- Poll backends are pluggable (only a dnotify-specific backend exists
now)
- Some refactoring

I'm attaching the patch, plus the new files. gam_poll.[ch] is now dead. 
We now have:

gam_poll_generic.[ch]
	all,busy,missing lists
	current_time stuff
	directory scanning code

gam_poll_dnotify.[ch]
	dnotify specific poll stuff (flow control)
	dnotify specific poll file


-- 
John McCutchan <ttb tentacle dhs org>
? .pc
? Makefile
? Makefile.in
? aclocal.m4
? autom4te.cache
? config.h
? config.h.in
? config.log
? config.status
? configure
? gamin.pc
? gamin.spec
? libtool
? patches
? stamp-h1
? doc/Makefile
? doc/Makefile.in
? lib/Makefile
? lib/Makefile.in
? libgamin/Makefile
? libgamin/Makefile.in
? libgamin/gam_error.c
? libgamin/gam_event.c
? libgamin/gamin
? python/Makefile
? python/Makefile.in
? python/gamin.pyc
? python/tests/Makefile
? python/tests/Makefile.in
? python/tests/python_diff
? python/tests/temp_dir
? server/Makefile
? server/Makefile.in
? server/gam_poll_defunct.c
? server/gam_poll_defunct.h
? server/gam_server
? tests/Makefile
? tests/Makefile.in
? tests/testgam
? tests/result/12
? tests/scenario/12.tst
Index: lib/gam_error.c
===================================================================
RCS file: /cvs/gnome/gamin/lib/gam_error.c,v
retrieving revision 1.7
diff -u -r1.7 gam_error.c
--- lib/gam_error.c	11 Aug 2005 18:41:43 -0000	1.7
+++ lib/gam_error.c	15 Aug 2005 18:38:56 -0000
@@ -3,6 +3,7 @@
 #include <stdarg.h>
 #include <stdlib.h>
 #include <signal.h>
+#include <errno.h>
 #include "gam_error.h"
 
 typedef void (*signal_handler)(int);
@@ -119,6 +120,12 @@
     va_end(args);
     if (debug_out)
 	fflush(debug_out);
+}
+
+int
+gam_errno(void)
+{
+	return (errno);
 }
 
 /**
Index: lib/gam_error.h
===================================================================
RCS file: /cvs/gnome/gamin/lib/gam_error.h,v
retrieving revision 1.5
diff -u -r1.5 gam_error.h
--- lib/gam_error.h	5 Aug 2005 12:08:55 -0000	1.5
+++ lib/gam_error.h	15 Aug 2005 18:38:56 -0000
@@ -45,6 +45,8 @@
 void	gam_error(const char *file, int line, const char* function,
                   const char* format, ...);
 
+int gam_errno(void);
+
 #ifdef GAM_DEBUG_ENABLED
 
 #ifdef GAMIN_DEBUG_API
Index: server/Makefile.am
===================================================================
RCS file: /cvs/gnome/gamin/server/Makefile.am,v
retrieving revision 1.20
diff -u -r1.20 Makefile.am
--- server/Makefile.am	4 Aug 2005 16:45:25 -0000	1.20
+++ server/Makefile.am	15 Aug 2005 18:38:56 -0000
@@ -27,8 +27,10 @@
 	gam_node.h					\
 	gam_tree.c					\
 	gam_tree.h					\
-	gam_poll.c					\
-	gam_poll.h					\
+	gam_poll_dnotify.c				\
+	gam_poll_dnotify.h				\
+	gam_poll_generic.c				\
+	gam_poll_generic.h				\
 	gam_pidname.c 					\
 	gam_pidname.h					\
 	gam_channel.c					\
Index: server/gam_dnotify.c
===================================================================
RCS file: /cvs/gnome/gamin/server/gam_dnotify.c,v
retrieving revision 1.24
diff -u -r1.24 gam_dnotify.c
--- server/gam_dnotify.c	10 Aug 2005 18:01:58 -0000	1.24
+++ server/gam_dnotify.c	15 Aug 2005 18:38:56 -0000
@@ -25,7 +25,7 @@
 #include <stdio.h>
 #include <glib.h>
 #include "gam_error.h"
-#include "gam_poll.h"
+#include "gam_poll_dnotify.h"
 #include "gam_dnotify.h"
 #include "gam_tree.h"
 #include "gam_event.h"
@@ -312,7 +312,7 @@
 
         GAM_DEBUG(DEBUG_INFO, "handling signal\n");
 
-        gam_poll_scan_directory(data->path);
+        gam_poll_generic_scan_directory(data->path);
         i++;
     }
 
@@ -349,7 +349,7 @@
     int fds[2];
     GSource *source;
 
-    g_return_val_if_fail(gam_poll_init_full(FALSE), FALSE);
+    g_return_val_if_fail(gam_poll_dnotify_init (), FALSE);
 
     if (pipe(fds) < 0) {
         g_warning("Could not create pipe.\n");
Index: server/gam_dnotify.h
===================================================================
RCS file: /cvs/gnome/gamin/server/gam_dnotify.h,v
retrieving revision 1.2
diff -u -r1.2 gam_dnotify.h
--- server/gam_dnotify.h	13 Jun 2005 09:13:45 -0000	1.2
+++ server/gam_dnotify.h	15 Aug 2005 18:38:56 -0000
@@ -3,7 +3,6 @@
 #define __MD_DNOTIFY_H__
 
 #include <glib.h>
-#include "gam_poll.h"
 #include "gam_subscription.h"
 
 G_BEGIN_DECLS
Index: server/gam_inotify.c
===================================================================
RCS file: /cvs/gnome/gamin/server/gam_inotify.c,v
retrieving revision 1.39
diff -u -r1.39 gam_inotify.c
--- server/gam_inotify.c	10 Aug 2005 15:34:19 -0000	1.39
+++ server/gam_inotify.c	15 Aug 2005 18:38:56 -0000
@@ -33,7 +33,7 @@
 #include "gam_debugging.h"
 #endif
 #include "gam_error.h"
-#include "gam_poll.h"
+#include "gam_poll_generic.h"
 #ifdef HAVE_LINUX_INOTIFY_H
 #include <linux/inotify.h>
 #else
@@ -932,7 +932,7 @@
     event_queue = g_queue_new ();
     events_to_process = g_queue_new ();
 
-	gam_poll_init ();
+	gam_poll_generic_init ();
 	gam_server_install_kernel_hooks (GAMIN_K_INOTIFY2,
 					 gam_inotify_add_subscription,
 					 gam_inotify_remove_subscription,
Index: server/gam_inotify.h
===================================================================
RCS file: /cvs/gnome/gamin/server/gam_inotify.h,v
retrieving revision 1.4
diff -u -r1.4 gam_inotify.h
--- server/gam_inotify.h	4 Aug 2005 16:50:53 -0000	1.4
+++ server/gam_inotify.h	15 Aug 2005 18:38:56 -0000
@@ -2,7 +2,6 @@
 #define __GAM_INOTIFY_H__
 
 #include <glib.h>
-#include "gam_poll.h"
 #include "gam_subscription.h"
 
 G_BEGIN_DECLS
Index: server/gam_node.c
===================================================================
RCS file: /cvs/gnome/gamin/server/gam_node.c,v
retrieving revision 1.17
diff -u -r1.17 gam_node.c
--- server/gam_node.c	11 Aug 2005 18:41:43 -0000	1.17
+++ server/gam_node.c	15 Aug 2005 18:38:56 -0000
@@ -346,6 +346,35 @@
     return node->pflags & flags;
 }
 
+void
+gam_node_emit_event (GamNode *node, GaminEventType event)
+{
+	GList *l;
+	GamNode *parent;
+	GList *subs;
+	int is_dir_node = gam_node_is_dir(node);
 
+#ifdef VERBOSE_POLL
+	GAM_DEBUG(DEBUG_INFO, "Poll: emit events %d for %s\n", event, gam_node_get_path(node));
+#endif
+	subs = gam_node_get_subscriptions(node);
+
+	if (subs)
+		subs = g_list_copy(subs);
+
+	parent = gam_node_parent(node);
+	if (parent) {
+		GList *parent_subs = gam_node_get_subscriptions(parent);
+
+		for (l = parent_subs; l; l = l->next) {
+			if (!g_list_find(subs, l->data))
+				subs = g_list_prepend(subs, l->data);
+		}
+	}
+
+	gam_server_emit_event(gam_node_get_path(node), is_dir_node, event, subs, 0);
+
+	g_list_free(subs);
+}
 
 /** @} */
Index: server/gam_node.h
===================================================================
RCS file: /cvs/gnome/gamin/server/gam_node.h,v
retrieving revision 1.10
diff -u -r1.10 gam_node.h
--- server/gam_node.h	11 Aug 2005 18:41:43 -0000	1.10
+++ server/gam_node.h	15 Aug 2005 18:38:56 -0000
@@ -99,6 +99,8 @@
 						     int             flags);
 
 
+void	gam_node_emit_event (GamNode *node, GaminEventType event);
+
 
 G_END_DECLS
 
Index: server/gam_server.c
===================================================================
RCS file: /cvs/gnome/gamin/server/gam_server.c,v
retrieving revision 1.40
diff -u -r1.40 gam_server.c
--- server/gam_server.c	11 Aug 2005 14:36:28 -0000	1.40
+++ server/gam_server.c	15 Aug 2005 18:38:56 -0000
@@ -32,7 +32,7 @@
 #include "gam_server.h"
 #include "gam_channel.h"
 #include "gam_subscription.h"
-#include "gam_poll.h"
+#include "gam_poll_generic.h"
 #ifdef ENABLE_INOTIFY
 #include "gam_inotify.h"
 #endif
@@ -63,6 +63,7 @@
 static gboolean (*__gam_poll_add_subscription) (GamSubscription *sub) = NULL;
 static gboolean (*__gam_poll_remove_subscription) (GamSubscription *sub) = NULL;
 static gboolean (*__gam_poll_remove_all_for) (GamListener *listener) = NULL;
+static GaminEventType (*__gam_poll_file) (GamNode *node) = NULL;
 
 #ifndef ENABLE_INOTIFY
 /**
@@ -118,7 +119,7 @@
 #ifdef ENABLE_DNOTIFY
     gam_dnotify_debug ();
 #endif
-    gam_poll_debug();
+    gam_poll_generic_debug();
 }
 
 /**
@@ -163,7 +164,7 @@
 #endif	
 	}
 
-	if (gam_poll_init()) {
+	if (gam_poll_generic_init()) {
 		GAM_DEBUG(DEBUG_INFO, "Using poll as backend\n");
 		return(TRUE);
 	}
@@ -407,12 +408,14 @@
 gam_server_install_poll_hooks (GamPollHandler name,
 				gboolean (*add)(GamSubscription *sub),
 				gboolean (*remove)(GamSubscription *sub),
-				gboolean (*remove_all)(GamListener *listener))
+				gboolean (*remove_all)(GamListener *listener),
+				GaminEventType (*poll_file)(GamNode *node))
 {
 	__gam_poll_handler = name;
 	__gam_poll_add_subscription = add;
 	__gam_poll_remove_subscription = remove;
 	__gam_poll_remove_all_for = remove_all;
+	__gam_poll_file = poll_file;
 }
 
 GamKernelHandler
@@ -493,6 +496,15 @@
 		return __gam_poll_remove_all_for (listener);
 
 	return FALSE;
+}
+
+GaminEventType
+gam_poll_file (GamNode *node)
+{
+	if (__gam_poll_file)
+		return __gam_poll_file (node);
+
+	return 0;
 }
 
 /**
Index: server/gam_server.h
===================================================================
RCS file: /cvs/gnome/gamin/server/gam_server.h,v
retrieving revision 1.8
diff -u -r1.8 gam_server.h
--- server/gam_server.h	10 Aug 2005 15:34:19 -0000	1.8
+++ server/gam_server.h	15 Aug 2005 18:38:56 -0000
@@ -4,6 +4,7 @@
 #include <glib.h>
 #include "gam_connection.h"
 #include "gam_subscription.h"
+#include "gam_node.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -20,7 +21,8 @@
 
 typedef enum {
 	GAMIN_P_NONE = 0,
-	GAMIN_P_DEFAULT = 1
+	GAMIN_P_DNOTIFY = 1,
+	GAMIN_P_BASIc = 2
 } GamPollHandler;
 
 typedef enum pollHandlerMode {
@@ -57,7 +59,8 @@
 void		gam_server_install_poll_hooks	(GamPollHandler name,
 						 gboolean (*add)(GamSubscription *sub),
 						 gboolean (*remove)(GamSubscription *sub),
-						 gboolean (*remove_all)(GamListener *listener));
+						 gboolean (*remove_all)(GamListener *listener),
+						 GaminEventType (*poll_file)(GamNode *node));
 
 
 GamKernelHandler gam_server_get_kernel_handler	(void);
@@ -72,6 +75,7 @@
 gboolean	gam_poll_add_subscription	(GamSubscription *sub);
 gboolean	gam_poll_remove_subscription	(GamSubscription *sub);
 gboolean	gam_poll_remove_all_for		(GamListener *listener);
+GaminEventType	gam_poll_file			(GamNode *node);
 
 #ifdef __cplusplus
 }
/* Gamin
 * Copyright (C) 2003 James Willcox, Corey Bowers
 * Copyright (C) 2004 Daniel Veillard
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "server_config.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <string.h>
#include <glib.h>
#include "fam.h"
#include "gam_error.h"
#include "gam_tree.h"
#include "gam_poll_dnotify.h"
#include "gam_event.h"
#include "gam_server.h"
#include "gam_protocol.h"
#include "gam_event.h"
#include "gam_excludes.h"

static gboolean gam_poll_dnotify_add_subscription(GamSubscription * sub);
static gboolean gam_poll_dnotify_remove_subscription(GamSubscription * sub);
static gboolean gam_poll_dnotify_remove_all_for(GamListener * listener);
static GaminEventType gam_poll_dnotify_poll_file(GamNode * node);
static gboolean gam_poll_dnotify_scan_callback(gpointer data);


gboolean
gam_poll_dnotify_init ()
{
	gam_poll_generic_init ();
	gam_server_install_poll_hooks (GAMIN_P_DNOTIFY,
				       gam_poll_dnotify_add_subscription,
				       gam_poll_dnotify_remove_subscription,
				       gam_poll_dnotify_remove_all_for,
				       gam_poll_dnotify_poll_file);

	g_timeout_add(1000, gam_poll_dnotify_scan_callback, NULL);
	return TRUE;
}

/**
 * gam_poll_delist_node:
 * @node: the node to delist
 *
 * This function is called when kernel monitoring for a node should
 * be turned off.
 */
static void
gam_poll_dnotify_delist_node(GamNode * node)
{
	GList *subs;
	const char *path;

	path = gam_node_get_path(node);

	if (gam_exclude_check(path) || gam_fs_get_mon_type (path) != GFS_MT_KERNEL)
		return;

	GAM_DEBUG(DEBUG_INFO, "poll-dnotify: Disabling kernel monitoring for %s\n", path);

	subs = gam_node_get_subscriptions(node);
	while (subs != NULL) {
		gam_poll_generic_trigger_handler (path, GAMIN_DESACTIVATE, node);
		subs = subs->next;
	}
}

/**
 * gam_poll_relist_node:
 * @node: the node to delist
 *
 * This function is called when kernel monitoring for a node should
 * be turned on (again).
 */
static void
gam_poll_dnotify_relist_node(GamNode * node)
{
	GList *subs;
	const char *path;

	path = gam_node_get_path(node);
	GAM_DEBUG(DEBUG_INFO, "poll-dnotify: Enabling kernel monitoring for %s\n", path);

	if (gam_exclude_check(path) || gam_fs_get_mon_type(path) != GFS_MT_KERNEL)
		return;

	subs = gam_node_get_subscriptions(node);

	while (subs != NULL) {
		gam_poll_generic_trigger_handler (path, GAMIN_ACTIVATE, node);
		subs = subs->next;
	}
}

/**
 * gam_poll_flowon_node:
 * @node: the node to delist
 *
 * This function is called when kernel monitoring flow control for a
 * node should be started
 */
static void
gam_poll_dnotify_flowon_node(GamNode * node)
{
	const char *path;

	path = gam_node_get_path(node);

	if (gam_exclude_check(path) || gam_fs_get_mon_type(path) != GFS_MT_KERNEL)
		return;

	GAM_DEBUG(DEBUG_INFO, "poll-dnotify: Enabling flow control for %s\n", path);

	gam_poll_generic_trigger_handler (path, GAMIN_FLOWCONTROLSTART, node);
}

/**
 * gam_poll_flowoff_node:
 * @node: the node to delist
 *
 * This function is called when kernel monitoring flow control for a
 * node should be started
 */
static void
gam_poll_dnotify_flowoff_node(GamNode * node)
{
	const char *path;

	path = gam_node_get_path(node);

	if (gam_exclude_check(path) || gam_fs_get_mon_type(path) != GFS_MT_KERNEL)
		return;

	GAM_DEBUG(DEBUG_INFO, "poll-dnotify: Disabling flow control for %s\n", path);

	gam_poll_generic_trigger_handler (path, GAMIN_FLOWCONTROLSTOP, node);
}

static GaminEventType
gam_poll_dnotify_poll_file(GamNode * node)
{
    GaminEventType event;
    struct stat sbuf;
    int stat_ret;
    const char *path;

    /* If not enough time has passed since the last time we polled this node, stop here */
    if (node->lasttime && gam_poll_generic_get_delta_time (node->lasttime) < node->poll_time)
        return 0;

    path = gam_node_get_path(node);
#ifdef VERBOSE_POLL
    GAM_DEBUG(DEBUG_INFO, "Poll: poll_file for %s called\n", path);
#endif

    memset(&sbuf, 0, sizeof(struct stat));
    if (node->lasttime == 0) {
        GAM_DEBUG(DEBUG_INFO, "Poll: file is new\n");
        stat_ret = stat(node->path, &sbuf);
        if (stat_ret != 0)
            gam_node_set_pflag (node, MON_MISSING);
        else
            gam_node_set_is_dir(node, (S_ISDIR(sbuf.st_mode) != 0));

        if (gam_exclude_check(path) || gam_fs_get_mon_type (path) != GFS_MT_KERNEL)
            gam_node_set_pflag (node, MON_NOKERNEL);

        memcpy(&(node->sbuf), &(sbuf), sizeof(struct stat));
        node->lasttime = gam_poll_generic_get_time ();

        if (stat_ret == 0)
            return 0;
        else
            return GAMIN_EVENT_DELETED;
    }

#ifdef VERBOSE_POLL
    GAM_DEBUG(DEBUG_INFO, " at %d delta %d : %d\n", current_time, current_time - node->lasttime, node->checks);
#endif

    event = 0;

    stat_ret = stat(node->path, &sbuf);
    if (stat_ret != 0) {
        if ((gam_errno() == ENOENT) && (!gam_node_has_pflag(node, MON_MISSING))) {
            /* deleted */
            gam_node_set_pflags (node, MON_MISSING);

            gam_poll_generic_remove_busy(node);
            if (gam_node_get_subscriptions(node) != NULL) {
                gam_poll_dnotify_delist_node(node);
                gam_poll_generic_add_missing(node);
            }
            event = GAMIN_EVENT_DELETED;
        }
    } else if (gam_node_has_pflag (node, MON_MISSING)) {
        /* created */
        gam_node_unset_pflag (node, MON_MISSING);
        event = GAMIN_EVENT_CREATED;
#ifdef ST_MTIM_NSEC
    } else if ((node->sbuf.st_mtim.tv_sec != sbuf.st_mtim.tv_sec) ||
           (node->sbuf.st_mtim.tv_nsec != sbuf.st_mtim.tv_nsec) ||
           (node->sbuf.st_size != sbuf.st_size) ||
           (node->sbuf.st_ctim.tv_sec != sbuf.st_ctim.tv_sec) ||
           (node->sbuf.st_ctim.tv_nsec != sbuf.st_ctim.tv_nsec))
    {
        event = GAMIN_EVENT_CHANGED;
    } else {
#ifdef VERBOSE_POLL
        GAM_DEBUG(DEBUG_INFO, "Poll: poll_file %s unchanged\n", path);
        GAM_DEBUG(DEBUG_INFO, "%d %d : %d %d\n", node->sbuf.st_mtim.tv_sec, node->sbuf.st_mtim.tv_nsec, sbuf.st_mtim.tv_sec, sbuf.st_mtim.tv_nsec);
#endif
#else
    } else if ((node->sbuf.st_mtime != sbuf.st_mtime) ||
           (node->sbuf.st_size != sbuf.st_size) ||
           (node->sbuf.st_ctime != sbuf.st_ctime))
    {
        event = GAMIN_EVENT_CHANGED;
#ifdef VERBOSE_POLL
        GAM_DEBUG(DEBUG_INFO, "%d : %d\n", node->sbuf.st_mtime, sbuf.st_mtime);
#endif
#endif
    }

    /*
    * TODO: handle the case where a file/dir is removed and replaced by
    *       a dir/file
    */
    if (stat_ret == 0)
        gam_node_set_is_dir(node, (S_ISDIR(sbuf.st_mode) != 0));

    memcpy(&(node->sbuf), &(sbuf), sizeof(struct stat));
    node->sbuf.st_mtime = sbuf.st_mtime; // VALGRIND!

    /*
    * if kernel monitoring prohibited, stop here
    */
    if (gam_node_has_pflag (node, MON_NOKERNEL))
        return (event);

    /*
    * load control, switch back to poll on very busy resources
    * and back when no update has happened in 5 seconds
    */
    if (gam_poll_generic_get_time() == node->lasttime) {
        if (!gam_node_has_pflag (node, MON_BUSY)) {
            if (node->sbuf.st_mtime == gam_poll_generic_get_time())
                node->checks++;
        }
    } else {
        node->lasttime = gam_poll_generic_get_time();
        if (gam_node_has_pflag (node, MON_BUSY)) {
            if (event == 0)
                node->checks++;
        } else {
            node->checks = 0;
        }
    }

    if ((node->checks >= 4) && (!gam_node_has_pflag (node, MON_BUSY))) {
        if ((gam_node_get_subscriptions(node) != NULL) &&
            (!gam_exclude_check(node->path) && gam_fs_get_mon_type (node->path) == GFS_MT_KERNEL))
        {
            GAM_DEBUG(DEBUG_INFO, "switching %s back to polling\n", path);
            gam_node_set_pflag (node, MON_BUSY);
            node->checks = 0;
            gam_poll_generic_add_busy(node);
            gam_poll_dnotify_flowon_node(node);
            /*
            * DNotify can be nasty here, we will miss events for parent dir
            * if we are not careful about it
            */
            if (!gam_node_is_dir(node)) {
                GamNode *parent = gam_node_parent(node);

                if ((parent != NULL) &&
                    (gam_node_get_subscriptions(parent) != NULL))
                {
                    gam_poll_generic_add_busy(parent);
                    /* gam_poll_flowon_node(parent); */
                }
            }
        }
    }

    if ((event == 0) && gam_node_has_pflag (node, MON_BUSY) && (node->checks > 5))
    {
        if ((gam_node_get_subscriptions(node) != NULL) &&
            (!gam_exclude_check(node->path) && gam_fs_get_mon_type (node->path) == GFS_MT_KERNEL))
        {
            GAM_DEBUG(DEBUG_INFO, "switching %s back to kernel monitoring\n", path);
            gam_node_unset_pflag (node, MON_BUSY);
            node->checks = 0;
            gam_poll_generic_remove_busy(node);
            gam_poll_dnotify_flowoff_node(node);
        }
    }

    return (event);
}

/**
 * node_add_subscription:
 * @node: the node tree pointer
 * @sub: the pointer to the subscription
 *
 * register a subscription for this node
 *
 * Returns 0 in case of success and -1 in case of failure
 */
static int
node_add_subscription(GamNode * node, GamSubscription * sub)
{
    if ((node == NULL) || (sub == NULL))
        return (-1);

    if ((node->path == NULL) || (node->path[0] != '/'))
        return (-1);

    GAM_DEBUG(DEBUG_INFO, "node_add_subscription(%s)\n", node->path);
    gam_node_add_subscription(node, sub);

    if (gam_exclude_check(node->path) || gam_fs_get_mon_type (node->path) == GFS_MT_POLL) {
        GAM_DEBUG(DEBUG_INFO, "  gam_exclude_check: true\n");
        if (node->lasttime == 0)
            gam_poll_dnotify_poll_file(node);

        gam_poll_generic_add_missing(node);
        return (0);
    }

	gam_poll_generic_trigger_handler (node->path, GAMIN_ACTIVATE, node);

    return (0);
}

/**
 * node_remove_subscription:
 * @node: the node tree pointer
 * @sub: the pointer to the subscription
 *
 * Removes a subscription for this node
 *
 * Returns 0 in case of success and -1 in case of failure
 */

static int
node_remove_subscription(GamNode * node, GamSubscription * sub)
{
    const char *path;

    if ((node == NULL) || (sub == NULL))
        return (-1);

    if ((node->path == NULL) || (node->path[0] != '/'))
        return (-1);

    GAM_DEBUG(DEBUG_INFO, "node_remove_subscription(%s)\n", node->path);

    gam_node_remove_subscription(node, sub);

    path = node->path;
    if (gam_exclude_check(path) || gam_fs_get_mon_type (path) == GFS_MT_POLL) {
        GAM_DEBUG(DEBUG_INFO, "  gam_exclude_check: true\n");
        return (0);
    }

    if (node->pflags == MON_BUSY) {
        GAM_DEBUG(DEBUG_INFO, "  node is busy\n");
    } else if (gam_node_has_pflags (node, MON_ALL_PFLAGS)) {
        GAM_DEBUG(DEBUG_INFO, "  node has flag %d\n", node->pflags);
        return (0);
    }

    /* DNotify makes our life miserable here */
	gam_poll_generic_trigger_handler (node->path, GAMIN_DESACTIVATE, node);

    return (0);
}

static gboolean
node_remove_directory_subscription(GamNode * node, GamSubscription * sub)
{
    GList *children, *l;
    gboolean remove_dir;

    GAM_DEBUG(DEBUG_INFO, "remove_directory_subscription %s\n",
              gam_node_get_path(node));

    node_remove_subscription(node, sub);

    remove_dir = (gam_node_get_subscriptions(node) == NULL);

    children = gam_tree_get_children(gam_poll_generic_get_tree(), node);
    for (l = children; l; l = l->next) {
        GamNode *child = (GamNode *) l->data;

        if ((!gam_node_get_subscriptions(child)) && (remove_dir) &&
            (!gam_tree_has_children(gam_poll_generic_get_tree(), child))) {
            gam_poll_generic_unregister_node (child);

            gam_tree_remove(gam_poll_generic_get_tree(), child);
        } else {
            remove_dir = FALSE;
        }
    }

    g_list_free(children);

    /*
     * do not remove the directory if the parent has a directory subscription
     */
    remove_dir = ((gam_node_get_subscriptions(node) == NULL) &&
                  (!gam_node_has_dir_subscriptions
                   (gam_node_parent(node))));

    if (remove_dir) {
        GAM_DEBUG(DEBUG_INFO, "  => remove_dir %s\n",
                  gam_node_get_path(node));
    }
    return remove_dir;
}


/**
 * Adds a subscription to be polled.
 *
 * @param sub a #GamSubscription to be polled
 * @returns TRUE if adding the subscription succeeded, FALSE otherwise
 */
static gboolean
gam_poll_dnotify_add_subscription(GamSubscription * sub)
{
    const char *path = gam_subscription_get_path (sub);
    GamNode *node = gam_tree_get_at_path (gam_poll_generic_get_tree(), path);
    int node_is_dir = FALSE;

    gam_listener_add_subscription(gam_subscription_get_listener(sub), sub);

	gam_poll_generic_update_time ();

    if (!node)
    {
        node = gam_tree_add_at_path(gam_poll_generic_get_tree(), path, gam_subscription_is_dir(sub));
    }

    if (node_add_subscription(node, sub) < 0)
    {
        gam_error(DEBUG_INFO, "Failed to add subscription for: %s\n", path);
        return FALSE;
    }

    node_is_dir = gam_node_is_dir(node);
    if (node_is_dir)
    {
        gam_poll_generic_first_scan_dir(sub, node, path);
    } else {
        GaminEventType event;

        event = gam_poll_dnotify_poll_file (node);
        GAM_DEBUG(DEBUG_INFO, "New file subscription: %s event %d\n", path, event);

        if ((event == 0) || (event == GAMIN_EVENT_EXISTS) ||
            (event == GAMIN_EVENT_CHANGED) ||
            (event == GAMIN_EVENT_CREATED))
        {
            if (gam_subscription_is_dir(sub)) {
                /* we are watching a file but requested a directory */
                gam_server_emit_one_event(path, node_is_dir, GAMIN_EVENT_DELETED, sub, 0);
            } else {
                gam_server_emit_one_event(path, node_is_dir, GAMIN_EVENT_EXISTS, sub, 0);
            }
        } else if (event != 0) {
            gam_server_emit_one_event(path, node_is_dir, GAMIN_EVENT_DELETED, sub, 0);
        }

        gam_server_emit_one_event(path, node_is_dir, GAMIN_EVENT_ENDEXISTS, sub, 0);
    }
    if (gam_node_has_pflag (node, MON_MISSING) || gam_node_has_pflag (node, MON_NOKERNEL))
        gam_poll_generic_add_missing(node);

    if (!node_is_dir) {
        char *parent;
        parent = g_path_get_dirname(path);
        node = gam_tree_get_at_path(gam_poll_generic_get_tree(), parent);
        if (!node)
        {
            node = gam_tree_add_at_path(gam_poll_generic_get_tree(), parent, gam_subscription_is_dir (sub));
        }
        g_free(parent);
    }

	gam_poll_generic_add (node);

    GAM_DEBUG(DEBUG_INFO, "Poll: added subscription\n");
    return TRUE;
}

/**
 * gam_poll_remove_subscription_real:
 * @sub: a subscription
 *
 * Implements the removal of a subscription, including
 * trimming the tree and deactivating the kernel back-end if needed.
 */
static void
gam_poll_dnotify_remove_subscription_real(GamSubscription * sub)
{
    GamNode *node;

    node = gam_tree_get_at_path(gam_poll_generic_get_tree(), gam_subscription_get_path(sub));

    if (node != NULL) {
        if (!gam_node_is_dir(node)) {
            GAM_DEBUG(DEBUG_INFO, "Removing node sub: %s\n",
                      gam_subscription_get_path(sub));
            node_remove_subscription(node, sub);

            if (!gam_node_get_subscriptions(node)) {
                GamNode *parent;

                gam_poll_generic_unregister_node (node);
                if (gam_tree_has_children(gam_poll_generic_get_tree(), node)) {
                    fprintf(stderr,
                            "node %s is not dir but has children\n",
                            gam_node_get_path(node));
                } else {
                    parent = gam_node_parent(node);
                    if ((parent != NULL) &&
                        (!gam_node_has_dir_subscriptions(parent))) {
                        gam_tree_remove(gam_poll_generic_get_tree(), node);

                        gam_poll_generic_prune_tree(parent);
                    }
                }
            }
        } else {
            GAM_DEBUG(DEBUG_INFO, "Removing directory sub: %s\n",
                      gam_subscription_get_path(sub));
            if (node_remove_directory_subscription(node, sub)) {
                GamNode *parent;

                gam_poll_generic_unregister_node (node);
                parent = gam_node_parent(node);
                if (!gam_tree_has_children(gam_poll_generic_get_tree(), node)) {
                    gam_tree_remove(gam_poll_generic_get_tree(), node);
                }

                gam_poll_generic_prune_tree(parent);
            }
        }
    }

    gam_subscription_free(sub);
}

/**
 * Removes a subscription which was being polled.
 *
 * @param sub a #GamSubscription to remove
 * @returns TRUE if removing the subscription succeeded, FALSE otherwise
 */
static gboolean
gam_poll_dnotify_remove_subscription(GamSubscription * sub)
{
    GamNode *node;

    node = gam_tree_get_at_path(gam_poll_generic_get_tree(), gam_subscription_get_path(sub));
    if (node == NULL) {
        /* free directly */
        gam_subscription_free(sub);
        return TRUE;
    }

    gam_subscription_cancel(sub);

    GAM_DEBUG(DEBUG_INFO, "Tree has %d nodes\n", gam_tree_get_size(gam_poll_generic_get_tree()));
    gam_poll_dnotify_remove_subscription_real(sub);
    GAM_DEBUG(DEBUG_INFO, "Tree has %d nodes\n", gam_tree_get_size(gam_poll_generic_get_tree()));

    GAM_DEBUG(DEBUG_INFO, "Poll: removed subscription\n");
    return TRUE;
}

/**
 * Stop polling all subscriptions for a given #GamListener.
 *
 * @param listener a #GamListener
 * @returns TRUE if removing the subscriptions succeeded, FALSE otherwise
 */
static gboolean
gam_poll_dnotify_remove_all_for(GamListener * listener)
{
    GList *subs, *l = NULL;

    subs = gam_listener_get_subscriptions(listener);

    for (l = subs; l; l = l->next) {
        GamSubscription *sub = l->data;

        g_assert(sub != NULL);

        gam_poll_remove_subscription(sub);
    }

    if (subs) {
        g_list_free(subs);
        return TRUE;
    } else
        return FALSE;
}

static gboolean
gam_poll_dnotify_scan_callback(gpointer data)
{
    int idx;
    static int in_poll_callback = 0;

#ifdef VERBOSE_POLL
    GAM_DEBUG(DEBUG_INFO, "gam_poll_scan_callback(): %d, %d missing, %d busy\n", in_poll_callback, g_list_length(missing_resources), g_list_length(busy_resources));
#endif
    if (in_poll_callback)
        return TRUE;

    in_poll_callback++;

	gam_poll_generic_update_time ();

    for (idx = 0;; idx++)
    {
        GamNode *node;

        /*
         * do not simply walk the list as it may be modified in the callback
         */
        node = (GamNode *) g_list_nth_data(gam_poll_generic_get_missing_list(), idx);

        if (node == NULL) {
#ifdef VERBOSE_POLL2
            GAM_DEBUG(DEBUG_INFO, "missing list node %d == NULL\n", idx);
#endif
            break;
        }
#ifdef VERBOSE_POLL
        GAM_DEBUG(DEBUG_INFO, "Checking missing file %s", node->path);
#endif
        if (node->is_dir) {
            gam_poll_generic_scan_directory_internal(node);
        } else {
            GaminEventType event;

            event = gam_poll_dnotify_poll_file (node);
            gam_node_emit_event(node, event);
        }

        /*
         * if the resource exists again and is not in a special monitoring
         * mode then switch back to dnotify for monitoring.
         */
        if (!gam_node_has_pflags (node, MON_ALL_PFLAGS) && (!gam_exclude_check(node->path) && gam_fs_get_mon_type (node->path) == GFS_MT_KERNEL))
        {
            gam_poll_generic_remove_missing(node);
            if (gam_node_get_subscriptions(node) != NULL) {
                gam_poll_dnotify_relist_node(node);
            }
        }
    }

    for (idx = 0;; idx++)
    {
        GamNode *node;

        /*
        * do not simply walk the list as it may be modified in the callback
        */
        node = (GamNode *) g_list_nth_data(gam_poll_generic_get_busy_list(), idx);

        if (node == NULL)
        {
#ifdef VERBOSE_POLL2
            GAM_DEBUG(DEBUG_INFO, "busy list node %d == NULL\n", idx);
#endif
            break;
        }
#ifdef VERBOSE_POLL
        GAM_DEBUG(DEBUG_INFO, "Checking busy file %s", node->path);
#endif
        if (node->is_dir) {
            gam_poll_generic_scan_directory_internal(node);
        } else {
            GaminEventType event;

            event = gam_poll_dnotify_poll_file (node);
            gam_node_emit_event(node, event);
        }

        /*
        * if the resource exists again and is not in a special monitoring
        * mode then switch back to dnotify for monitoring.
        */
        if (!gam_node_has_pflags (node, MON_ALL_PFLAGS) && (!gam_exclude_check(node->path) && gam_fs_get_mon_type (node->path) == GFS_MT_KERNEL))
        {
            gam_poll_generic_remove_busy(node);
            if (gam_node_get_subscriptions(node) != NULL) {
                gam_poll_dnotify_flowoff_node(node);
            }
        }
    }
    in_poll_callback = 0;
    return TRUE;
}
#ifndef __GAM_POLL_DNOTIFY_H
#define __GAM_POLL_DNOTIFY_H

#include "gam_poll_generic.h"

G_BEGIN_DECLS

gboolean	gam_poll_dnotify_init	(void);

G_END_DECLS

#endif
/* Gamin
 * Copyright (C) 2003 James Willcox, Corey Bowers
 * Copyright (C) 2004 Daniel Veillard
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "server_config.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <glib.h>
#include "fam.h"
#include "gam_error.h"
#include "gam_tree.h"
#include "gam_poll_generic.h"
#include "gam_event.h"
#include "gam_server.h"
#include "gam_protocol.h"
#include "gam_event.h"
#include "gam_excludes.h"

//#define VERBOSE_POLL
//#define VERBOSE_POLL2

#define DEFAULT_POLL_TIMEOUT 1

static GamTree *	tree = NULL;
static GList *		missing_resources = NULL;
static GList *		busy_resources = NULL;
static GList *		all_resources = NULL;
static GList *		dead_resources = NULL;
static time_t		current_time = 0;

gboolean
gam_poll_generic_init()
{
	tree = gam_tree_new ();
	gam_poll_generic_update_time ();
	return TRUE;
}

static void
gam_poll_debug_node(GamNode * node, gpointer user_data)
{
    if (node == NULL)
        return;

    GAM_DEBUG(DEBUG_INFO, "dir %d flags %d pflags %d nb subs %d : %s\n", node->is_dir, node->flags, node->pflags, g_list_length(node->subs), node->path);
}

void
gam_poll_generic_debug(void)
{
    if (missing_resources != NULL) {
        GAM_DEBUG(DEBUG_INFO, "Dumping poll missing resources\n");
        g_list_foreach(missing_resources, (GFunc) gam_poll_debug_node, NULL);
    } else {
        GAM_DEBUG(DEBUG_INFO, "No poll missing resources\n");
    }

    if (busy_resources != NULL) {
        GAM_DEBUG(DEBUG_INFO, "Dumping poll busy resources\n");
        g_list_foreach(busy_resources, (GFunc) gam_poll_debug_node, NULL);
    } else {
        GAM_DEBUG(DEBUG_INFO, "No poll busy resources\n");
    }

    if (all_resources != NULL) {
        GAM_DEBUG(DEBUG_INFO, "Dumping poll all resources\n");
        g_list_foreach(all_resources, (GFunc) gam_poll_debug_node, NULL);
    } else {
        GAM_DEBUG(DEBUG_INFO, "No poll all resources\n");
    }
}

/**
 * gam_poll_generic_add_missing:
 * @node: a missing node
 *
 * Add a missing node to the list for polling its creation.
 */
void
gam_poll_generic_add_missing(GamNode * node)
{
	if (g_list_find(missing_resources, node) == NULL) {
		missing_resources = g_list_prepend(missing_resources, node);
		GAM_DEBUG(DEBUG_INFO, "Poll: adding missing node %s\n", gam_node_get_path(node));
	}
}



/**
 * gam_poll_generic_remove_missing:
 * @node: a missing node
 *
 * Remove a missing node from the list.
 */
void
gam_poll_generic_remove_missing(GamNode * node)
{
	g_assert (g_list_find (missing_resources, node));
	GAM_DEBUG(DEBUG_INFO, "Poll: removing missing node %s\n", gam_node_get_path(node));
	missing_resources = g_list_remove_all(missing_resources, node);
}

/**
 * gam_poll_generic_add_busy:
 * @node: a busy node
 *
 * Add a busy node to the list for polling its creation.
 */
void
gam_poll_generic_add_busy(GamNode * node)
{
	if (g_list_find(busy_resources, node) == NULL) {
		busy_resources = g_list_prepend(busy_resources, node);
		GAM_DEBUG(DEBUG_INFO, "Poll: adding busy node %s\n", gam_node_get_path(node));
	}
}

/**
 * gam_poll_generic_remove_busy:
 * @node: a busy node
 *
 * Remove a busy node from the list.
 */
void
gam_poll_generic_remove_busy(GamNode * node)
{
	if (!g_list_find (busy_resources, node))
		return;

	GAM_DEBUG(DEBUG_INFO, "Poll: removing busy node %s\n", gam_node_get_path(node));
	busy_resources = g_list_remove_all(busy_resources, node);
}

void
gam_poll_generic_add (GamNode * node)
{
	if (g_list_find (all_resources, node) == NULL)
	{
		all_resources = g_list_prepend(all_resources, node);
		GAM_DEBUG(DEBUG_INFO, "Poll: Adding node %s\n", gam_node_get_path (node));
	}
}

void
gam_poll_generic_remove (GamNode * node)
{
	g_assert (g_list_find (all_resources, node));
	GAM_DEBUG(DEBUG_INFO, "Poll: removing node %s\n", gam_node_get_path(node));
	all_resources = g_list_remove_all(all_resources, node);
}

time_t
gam_poll_generic_get_time()
{
	return current_time;
}

void
gam_poll_generic_update_time()
{
	current_time = time (NULL);
}

time_t
gam_poll_generic_get_delta_time(time_t pt)
{
	if (current_time >= pt)
		return current_time - pt;
	/* FIXME: We have wrapped */
	return 0;
}

static void
gam_poll_generic_trigger_file_handler (const char *path, pollHandlerMode mode, GamNode *node)
{
    if (node->mon_type != GFS_MT_KERNEL)
        return;

	if (gam_server_get_kernel_handler() == GAMIN_K_DNOTIFY || gam_server_get_kernel_handler() == GAMIN_K_INOTIFY) {
		if (gam_node_is_dir(node)) {
			gam_kernel_file_handler (path, mode);
		} else {
			const char *dir = NULL;
			GamNode *parent = gam_node_parent(node);

			if (!parent)
				return;

			dir = parent->path;
			switch (mode) {
			case GAMIN_ACTIVATE:
				GAM_DEBUG(DEBUG_INFO, "poll: Activating kernel monitoring on %s\n", dir);
				gam_kernel_dir_handler (dir, mode);
			break;
			case GAMIN_DESACTIVATE:
				GAM_DEBUG(DEBUG_INFO, "poll: Deactivating kernel monitoring on %s\n", dir);
				gam_kernel_dir_handler (dir, mode);
			break;
			case GAMIN_FLOWCONTROLSTART:
				if (!gam_node_has_pflag (parent, MON_BUSY)) {
					GAM_DEBUG(DEBUG_INFO, "poll: marking busy on %s\n", dir);
					gam_kernel_dir_handler (dir, mode);
					gam_poll_generic_add_busy(parent);
					gam_node_set_pflag (parent, MON_BUSY);
				}
			break;
			case GAMIN_FLOWCONTROLSTOP:
				if (gam_node_has_pflag (parent, MON_BUSY)) {
					GAM_DEBUG(DEBUG_INFO, "poll: unmarking busy on %s\n", dir);
					gam_kernel_dir_handler (dir, mode);
					gam_poll_generic_remove_busy(parent);
					gam_node_unset_pflag (parent, MON_BUSY);
				}
			break;
			}
		}
	} else {
		gam_kernel_file_handler (path, mode);
	}
}


static void
gam_poll_generic_trigger_dir_handler (const char *path, pollHandlerMode mode, GamNode *node)
{
	if (node->mon_type != GFS_MT_KERNEL)
		return;

	if (gam_server_get_kernel_handler() == GAMIN_K_DNOTIFY || gam_server_get_kernel_handler() == GAMIN_K_INOTIFY) {
		if (gam_node_is_dir(node)) {
			gam_kernel_dir_handler (path, mode);
		} else {
			gam_poll_generic_trigger_file_handler(path, mode, node);
		}
	} else {
		gam_kernel_dir_handler (path, mode);
	}
}


void
gam_poll_generic_trigger_handler(const char *path, pollHandlerMode mode, GamNode *node)
{
	if (gam_node_is_dir(node))
		gam_poll_generic_trigger_dir_handler(node->path, mode, node);
	else
		gam_poll_generic_trigger_file_handler(node->path, mode, node);
}

void
gam_poll_generic_scan_directory_internal (GamNode *dir_node)
{
	GDir *dir;
	const char *name, *dpath;
	char *path;
	GamNode *node;
	GaminEventType event = 0, fevent;
	GList *children, *l;
	unsigned int exists = 0;
	int is_dir_node;

	if (dir_node == NULL)
		return;

	dpath = gam_node_get_path(dir_node);

	if (dpath == NULL)
		return;

	if (!gam_node_get_subscriptions(dir_node))
		goto scan_files;

	event = gam_poll_file(dir_node);

	if (event != 0)
		gam_node_emit_event (dir_node, event);

	dir = g_dir_open(dpath, 0, NULL);

	if (dir == NULL) {
#ifdef VERBOSE_POLL
		GAM_DEBUG(DEBUG_INFO, "Poll: directory %s is not readable or missing\n", dpath);
#endif
		return;
	}

	exists = 1;

#ifdef VERBOSE_POLL
	GAM_DEBUG(DEBUG_INFO, "Poll: scanning directory %s\n", dpath);
#endif
	while ((name = g_dir_read_name(dir)) != NULL) {
		path = g_build_filename(gam_node_get_path(dir_node), name, NULL);

		node = gam_tree_get_at_path(tree, path);

		if (!node) {
			if (!g_file_test(path, G_FILE_TEST_IS_DIR)) {
				node = gam_node_new(path, NULL, FALSE);
				gam_tree_add(tree, dir_node, node);
				gam_node_set_flag(node, FLAG_NEW_NODE);
			} else {
				node = gam_node_new(path, NULL, TRUE);
				gam_tree_add(tree, dir_node, node);
				gam_node_set_flag(node, FLAG_NEW_NODE);
			}
		}

		g_free(path);
	}

	g_dir_close(dir);

scan_files:


	/* FIXME: Shouldn't is_dir_node be assigned inside the loop? */
	is_dir_node = gam_node_is_dir(dir_node);
	children = gam_tree_get_children(tree, dir_node);
	for (l = children; l; l = l->next) {
		node = (GamNode *) l->data;

		fevent = gam_poll_file(node);

		if (gam_node_has_flag(node, FLAG_NEW_NODE)) {
			if (is_dir_node && gam_node_get_subscriptions(node)) {
				gam_node_unset_flag(node, FLAG_NEW_NODE);
				gam_poll_generic_scan_directory_internal(node);
			} else {
				gam_node_unset_flag(node, FLAG_NEW_NODE);
				fevent = GAMIN_EVENT_CREATED;
			}
		}

		if (fevent != 0) {
			gam_node_emit_event (node, fevent);
		} else {
			/* just send the EXIST events if the node exists */

			if (!gam_node_has_pflag (node, MON_MISSING))
			{
				gam_server_emit_event(gam_node_get_path(node),
				gam_node_is_dir(node),
				GAMIN_EVENT_EXISTS, NULL, 0);
			}
		}
	}

	g_list_free(children);
}

/**
 * Scans a directory for changes, and emits events if needed.
 *
 * @param path the path to the directory to be scanned
 */
void
gam_poll_generic_scan_directory(const char *path)
{
	GamNode *node;

	gam_poll_generic_update_time ();

	node = gam_tree_get_at_path(tree, path);
	if (node == NULL)
		node = gam_tree_add_at_path(tree, path, TRUE);

	if (node == NULL) {
		gam_error(DEBUG_INFO, "gam_tree_add_at_path(%s) returned NULL\n", path);
		return;
	}

	gam_poll_generic_scan_directory_internal(node);
}

/**
 * First dir scanning on a new subscription, generates the Exists EndExists
 * events.
 */
void
gam_poll_generic_first_scan_dir (GamSubscription * sub, GamNode * dir_node, const char *dpath)
{
	GDir *dir;
	char *path;
	GList *subs;
	int with_exists = 1;
	const char *name;
	GamNode *node;

	GAM_DEBUG(DEBUG_INFO, "Looking for existing files in: %s\n", dpath);

	if (gam_subscription_has_option(sub, GAM_OPT_NOEXISTS))
	{
		with_exists = 0;
		GAM_DEBUG(DEBUG_INFO, "   Exists not wanted\n");
	}

	subs = g_list_prepend(NULL, sub);

	if (!g_file_test(dpath, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
		GAM_DEBUG(DEBUG_INFO, "Monitoring missing dir: %s\n", dpath);

		gam_server_emit_event(dpath, 1, GAMIN_EVENT_DELETED, subs, 1);

		stat(dir_node->path, &(dir_node->sbuf));
		dir_node->lasttime = gam_poll_generic_get_time ();

		if (g_file_test(dpath, G_FILE_TEST_EXISTS)) {
			gam_node_set_pflags (dir_node, MON_WRONG_TYPE);
			dir_node->is_dir = 0;
		} else {
			gam_node_set_pflags (dir_node, MON_MISSING);
			gam_poll_generic_add_missing(dir_node);
		}
		goto done;
	}

	if (dir_node->lasttime == 0)
		gam_poll_file(dir_node);

	if (with_exists)
		gam_server_emit_event(dpath, 1, GAMIN_EVENT_EXISTS, subs, 1);


	dir = g_dir_open(dpath, 0, NULL);

	if (dir == NULL) {
		goto done;
	}

	while ((name = g_dir_read_name(dir)) != NULL)
	{
		path = g_build_filename(dpath, name, NULL);

		node = gam_tree_get_at_path(tree, path);

		if (!node)
		{
			GAM_DEBUG(DEBUG_INFO, "Unregistered node %s\n", path);
			if (!g_file_test(path, G_FILE_TEST_IS_DIR)) {
				node = gam_node_new(path, NULL, FALSE);
			} else {
				node = gam_node_new(path, NULL, TRUE);
			}
			stat(node->path, &(node->sbuf));
			gam_node_set_is_dir(node, (S_ISDIR(node->sbuf.st_mode) != 0));

			if (gam_exclude_check(path) || gam_fs_get_mon_type(path) != GFS_MT_KERNEL)
				gam_node_set_pflag (node, MON_NOKERNEL);

			node->lasttime = gam_poll_generic_get_time ();
			gam_tree_add(tree, dir_node, node);
		}

		if (with_exists)
			gam_server_emit_event(name, 1, GAMIN_EVENT_EXISTS, subs, 1);

		g_free(path);
	}

	g_dir_close(dir);

done:
	if (with_exists)
		gam_server_emit_event(dpath, 1, GAMIN_EVENT_ENDEXISTS, subs, 1);

	g_list_free(subs);

	GAM_DEBUG(DEBUG_INFO, "Done scanning %s\n", dpath);
}

GamTree *
gam_poll_generic_get_tree()
{
	return tree;
}

GList *
gam_poll_generic_get_missing_list (void)
{
	return missing_resources;
}

GList *
gam_poll_generic_get_busy_list (void)
{
	return busy_resources;
}

GList *
gam_poll_generic_get_all_list (void)
{
	return all_resources;
}

GList *
gam_poll_generic_get_dead_list (void)
{
	return dead_resources;
}

void
gam_poll_generic_unregister_node (GamNode * node)
{
	if (missing_resources != NULL) {
		gam_poll_generic_remove_missing(node);
	}

	if (busy_resources != NULL) {
		gam_poll_generic_remove_busy(node);
	}

	if (all_resources != NULL) {
		all_resources = g_list_remove(all_resources, node);
	}
}

void
gam_poll_generic_prune_tree(GamNode * node)
{
	/* don't prune the root */
	if (gam_node_parent(node) == NULL)
		return;

	if (!gam_tree_has_children(tree, node) && !gam_node_get_subscriptions(node)) 
	{
		GamNode *parent;

		GAM_DEBUG(DEBUG_INFO, "prune_tree: %s\n", gam_node_get_path(node));

		parent = gam_node_parent(node);
		gam_poll_generic_unregister_node(node);
		gam_tree_remove(tree, node);
		gam_poll_generic_prune_tree(parent);
	}
}

#ifndef __GAM_POLL_GENERIC_H
#define __GAM_POLL_GENERIC_H

#include <glib.h>
#include "gam_server.h"
#include "gam_tree.h"

G_BEGIN_DECLS

gboolean	gam_poll_generic_init			(void);
void		gam_poll_generic_debug			(void);

void		gam_poll_generic_add_missing	(GamNode * node);
void		gam_poll_generic_remove_missing	(GamNode * node);
void		gam_poll_generic_add_busy	(GamNode * node);
void		gam_poll_generic_remove_busy	(GamNode * node);
void		gam_poll_generic_add		(GamNode * node);
void		gam_poll_generic_remove		(GamNode * node);

time_t		gam_poll_generic_get_time		(void);
void		gam_poll_generic_update_time	(void);
time_t		gam_poll_generic_get_delta_time	(time_t pt);

void		gam_poll_generic_trigger_handler(const char *path, pollHandlerMode mode, GamNode *node);

void		gam_poll_generic_scan_directory	(const char *path);
void		gam_poll_generic_scan_directory_internal (GamNode *dir_node);
void		gam_poll_generic_first_scan_dir	(GamSubscription * sub, GamNode * dir_node, const char *dpath);

GamTree *	gam_poll_generic_get_tree		(void);
GList *		gam_poll_generic_get_missing_list (void);
GList *		gam_poll_generic_get_busy_list (void);
GList *		gam_poll_generic_get_all_list (void);
GList *		gam_poll_generic_get_dead_list (void);

void		gam_poll_generic_unregister_node (GamNode * node);
void		gam_poll_generic_prune_tree (GamNode * node);

G_END_DECLS

#endif


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