From 7dc75d707cb5d1a2ea33101ab540dd6861045f4f Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Fri, 14 Mar 2008 08:58:24 +0000 Subject: [PATCH] Added. Added. Added. Added. Added. Added. Added. Added. Added. Added. 2008-03-14 Alexander Larsson * Makefile.am: * fen/Makefile.am: Added. * fen/fen-data.[ch]: Added. * fen/fen-dump.[ch]: Added. * fen/fen-helper.[ch]: Added. * fen/fen-kernel.[ch]: Added. * fen/fen-missing.[ch]: Added. * fen/fen-node.[ch]: Added. * fen/fen-sub.[ch]: Added. * fen/gfendirectorymonitor.[ch]: Added. * fen/gfenfilemonitor.[ch]: Added. * giomodule.c: Added Solaris FEN file notification backend. Patch from Lin Ma svn path=/trunk/; revision=6702 --- ChangeLog | 5 + configure.in | 17 + gio/ChangeLog | 17 + gio/Makefile.am | 6 + gio/fen/Makefile.am | 38 +++ gio/fen/fen-data.c | 708 +++++++++++++++++++++++++++++++++++++++++ gio/fen/fen-data.h | 88 +++++ gio/fen/fen-dump.c | 99 ++++++ gio/fen/fen-dump.h | 28 ++ gio/fen/fen-helper.c | 330 +++++++++++++++++++ gio/fen/fen-helper.h | 35 ++ gio/fen/fen-kernel.c | 523 ++++++++++++++++++++++++++++++ gio/fen/fen-kernel.h | 53 +++ gio/fen/fen-missing.c | 114 +++++++ gio/fen/fen-missing.h | 37 +++ gio/fen/fen-node.c | 460 ++++++++++++++++++++++++++ gio/fen/fen-node.h | 72 +++++ gio/fen/fen-sub.c | 41 +++ gio/fen/fen-sub.h | 38 +++ gio/fen/gfendirectorymonitor.c | 149 +++++++++ gio/fen/gfendirectorymonitor.h | 55 ++++ gio/fen/gfenfilemonitor.c | 148 +++++++++ gio/fen/gfenfilemonitor.h | 56 ++++ gio/giomodule.c | 8 +- 24 files changed, 3122 insertions(+), 3 deletions(-) create mode 100644 gio/fen/Makefile.am create mode 100644 gio/fen/fen-data.c create mode 100644 gio/fen/fen-data.h create mode 100644 gio/fen/fen-dump.c create mode 100644 gio/fen/fen-dump.h create mode 100644 gio/fen/fen-helper.c create mode 100644 gio/fen/fen-helper.h create mode 100644 gio/fen/fen-kernel.c create mode 100644 gio/fen/fen-kernel.h create mode 100644 gio/fen/fen-missing.c create mode 100644 gio/fen/fen-missing.h create mode 100644 gio/fen/fen-node.c create mode 100644 gio/fen/fen-node.h create mode 100644 gio/fen/fen-sub.c create mode 100644 gio/fen/fen-sub.h create mode 100644 gio/fen/gfendirectorymonitor.c create mode 100644 gio/fen/gfendirectorymonitor.h create mode 100644 gio/fen/gfenfilemonitor.c create mode 100644 gio/fen/gfenfilemonitor.h diff --git a/ChangeLog b/ChangeLog index ee1b71e..71d4ece 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2008-03-14 Alexander Larsson + + * configure.in: + Added checks for FEN (Solaris File Event Notification) + 2008-03-13 Tor Lillqvist * glib-zip.in: Add lib/gio-2.0.lib and lib/gio-2.0.def. diff --git a/configure.in b/configure.in index b10ebb1..2d6815c 100644 --- a/configure.in +++ b/configure.in @@ -1502,6 +1502,22 @@ AC_CHECK_HEADERS([sys/inotify.h], AM_CONDITIONAL(HAVE_INOTIFY, [test "$inotify_support" = "yes"]) +dnl ********************************* +dnl ** Check for Solaris FEN (GIO) ** +dnl ********************************* +fen_support=no +AC_COMPILE_IFELSE([ +#include +#ifndef PORT_SOURCE_FILE +#error "Please upgrade to Nevada 72 or above to suppoert FEN" +#endif +int main() { return 0; } ], +[ + fen_support=yes +],) + +AM_CONDITIONAL(HAVE_FEN, [test "$fen_support" = "yes"]) + dnl **************************** dnl *** Checks for FAM (GIO) *** dnl **************************** @@ -3234,6 +3250,7 @@ gthread/Makefile gio/Makefile gio/xdgmime/Makefile gio/inotify/Makefile +gio/fen/Makefile gio/fam/Makefile gio/win32/Makefile gio/tests/Makefile diff --git a/gio/ChangeLog b/gio/ChangeLog index b8ef15f..8b890ea 100644 --- a/gio/ChangeLog +++ b/gio/ChangeLog @@ -1,3 +1,20 @@ +2008-03-14 Alexander Larsson + + * Makefile.am: + * fen/Makefile.am: Added. + * fen/fen-data.[ch]: Added. + * fen/fen-dump.[ch]: Added. + * fen/fen-helper.[ch]: Added. + * fen/fen-kernel.[ch]: Added. + * fen/fen-missing.[ch]: Added. + * fen/fen-node.[ch]: Added. + * fen/fen-sub.[ch]: Added. + * fen/gfendirectorymonitor.[ch]: Added. + * fen/gfenfilemonitor.[ch]: Added. + * giomodule.c: + Added Solaris FEN file notification backend. + Patch from Lin Ma + 2008-03-13 Tor Lillqvist * Makefile.am: Actually use the gio.def file when linking the diff --git a/gio/Makefile.am b/gio/Makefile.am index 7c625aa..3f336c2 100644 --- a/gio/Makefile.am +++ b/gio/Makefile.am @@ -99,6 +99,12 @@ SUBDIRS += inotify platform_libadd += inotify/libinotify.la endif +if HAVE_FEN +AM_CPPFLAGS += -DHAVE_FEN +SUBDIRS += fen +platform_libadd += fen/libfen.la +endif + if OS_WIN32 SUBDIRS += win32 platform_libadd += win32/libgiowin32.la diff --git a/gio/fen/Makefile.am b/gio/fen/Makefile.am new file mode 100644 index 0000000..1acaa44 --- /dev/null +++ b/gio/fen/Makefile.am @@ -0,0 +1,38 @@ +include $(top_srcdir)/Makefile.decl + +NULL = + +noinst_LTLIBRARIES = libfen.la + +libfen_la_SOURCES = \ + fen-dump.c \ + fen-dump.h \ + fen-kernel.c \ + fen-kernel.h \ + fen-node.c \ + fen-node.h \ + fen-missing.c \ + fen-missing.h \ + fen-helper.c \ + fen-helper.h \ + fen-data.c \ + fen-data.h \ + fen-sub.c \ + fen-sub.h \ + gfenfilemonitor.c \ + gfenfilemonitor.h \ + gfendirectorymonitor.c \ + gfendirectorymonitor.h \ + $(NULL) + +libfen_la_CFLAGS = \ + -DG_LOG_DOMAIN=\"GLib-GIO\" \ + -I$(top_srcdir) \ + -I$(top_srcdir)/glib \ + -I$(top_srcdir)/gmodule \ + -I$(top_srcdir)/gio \ + -I$(top_builddir)/gio \ + $(GLIB_DEBUG_FLAGS) \ + -DGIO_MODULE_DIR=\"$(GIO_MODULE_DIR)\" \ + -DGIO_COMPILATION \ + -DG_DISABLE_DEPRECATED diff --git a/gio/fen/fen-data.c b/gio/fen/fen-data.c new file mode 100644 index 0000000..a094add --- /dev/null +++ b/gio/fen/fen-data.c @@ -0,0 +1,708 @@ +/* -*- 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 + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#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 +static gboolean fd_debug_enabled = FALSE; + +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: /* Ignored */ + case FILE_DELETE: + 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 (tail->e == FILE_ATTRIB && ev->e == FILE_MODIFIED) { + ev->has_twin = TRUE; + fnode_event_delete (tail); + } else if (ev->e == FILE_ATTRIB) { + 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/gio/fen/fen-data.h b/gio/fen/fen-data.h new file mode 100644 index 0000000..842358b --- /dev/null +++ b/gio/fen/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 + */ + +#include +#include +#include +#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/gio/fen/fen-dump.c b/gio/fen/fen-dump.c new file mode 100644 index 0000000..cd4de28 --- /dev/null +++ b/gio/fen/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 + */ + +#include "config.h" +#include +#include +#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/gio/fen/fen-dump.h b/gio/fen/fen-dump.h new file mode 100644 index 0000000..a4b0e81 --- /dev/null +++ b/gio/fen/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 + */ + +#ifndef _FEN_DUMP_H_ +#define _FEN_DUMP_H_ + + +#endif /* _FEN_DUMP_H_ */ diff --git a/gio/fen/fen-helper.c b/gio/fen/fen-helper.c new file mode 100644 index 0000000..381dd52 --- /dev/null +++ b/gio/fen/fen-helper.c @@ -0,0 +1,330 @@ +/* -*- 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 + */ + +#include "config.h" +#include +#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 +static gboolean fh_debug_enabled = FALSE; + +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/gio/fen/fen-helper.h b/gio/fen/fen-helper.h new file mode 100644 index 0000000..75fa0df --- /dev/null +++ b/gio/fen/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 + */ + +#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/gio/fen/fen-kernel.c b/gio/fen/fen-kernel.c new file mode 100644 index 0000000..cdcecc1 --- /dev/null +++ b/gio/fen/fen-kernel.c @@ -0,0 +1,523 @@ +/* -*- 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 + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include "fen-kernel.h" +#include "fen-dump.h" + +#define FK_W if (fk_debug_enabled) g_warning +static gboolean fk_debug_enabled = FALSE; + +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; /* */ +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/gio/fen/fen-kernel.h b/gio/fen/fen-kernel.h new file mode 100644 index 0000000..cf3ae42 --- /dev/null +++ b/gio/fen/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 + */ + +#include +#include +#include + +#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/gio/fen/fen-missing.c b/gio/fen/fen-missing.c new file mode 100644 index 0000000..84662c2 --- /dev/null +++ b/gio/fen/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 + */ + +#include "config.h" +#include +#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/gio/fen/fen-missing.h b/gio/fen/fen-missing.h new file mode 100644 index 0000000..89e4389 --- /dev/null +++ b/gio/fen/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 + */ + +#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/gio/fen/fen-node.c b/gio/fen/fen-node.c new file mode 100644 index 0000000..7285c72 --- /dev/null +++ b/gio/fen/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 + */ + +#include "config.h" +#include +#include +#include +#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/gio/fen/fen-node.h b/gio/fen/fen-node.h new file mode 100644 index 0000000..7c16762 --- /dev/null +++ b/gio/fen/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 + */ + +#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/gio/fen/fen-sub.c b/gio/fen/fen-sub.c new file mode 100644 index 0000000..373cba5 --- /dev/null +++ b/gio/fen/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 + */ + +#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/gio/fen/fen-sub.h b/gio/fen/fen-sub.h new file mode 100644 index 0000000..1f4bb5e --- /dev/null +++ b/gio/fen/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 + */ + +#include + +#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/gio/fen/gfendirectorymonitor.c b/gio/fen/gfendirectorymonitor.c new file mode 100644 index 0000000..2262196 --- /dev/null +++ b/gio/fen/gfendirectorymonitor.c @@ -0,0 +1,149 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim:set expandtab ts=4 shiftwidth=4: */ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2006-2007 Red Hat, Inc. + * Copyright (C) 2007 Sebastian Dröge. + * 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: Alexander Larsson + * John McCutchan + * Sebastian Dröge + * Lin Ma + */ + +#include + +#include "gfendirectorymonitor.h" +#include "giomodule.h" + +#include "fen-helper.h" + +#include "gioalias.h" + +struct _GFenDirectoryMonitor +{ + GLocalDirectoryMonitor parent_instance; + gboolean cancelled; + fen_sub* sub; +}; + +static gboolean g_fen_directory_monitor_cancel (GFileMonitor* monitor); + +#define g_fen_directory_monitor_get_type _g_fen_directory_monitor_get_type +G_DEFINE_TYPE_WITH_CODE (GFenDirectoryMonitor, g_fen_directory_monitor, G_TYPE_LOCAL_DIRECTORY_MONITOR, + g_io_extension_point_implement (G_LOCAL_DIRECTORY_MONITOR_EXTENSION_POINT_NAME, + g_define_type_id, + "fen", + 20)) + +static void +g_fen_directory_monitor_finalize (GObject *object) +{ + GFenDirectoryMonitor *self = G_FEN_DIRECTORY_MONITOR (object); + + if (self->sub) { + fen_remove (G_LOCAL_DIRECTORY_MONITOR (self)->dirname, self->sub, TRUE); + fen_sub_delete (self->sub); + self->sub = NULL; + } + + if (G_OBJECT_CLASS (g_fen_directory_monitor_parent_class)->finalize) + (*G_OBJECT_CLASS (g_fen_directory_monitor_parent_class)->finalize) (object); +} + +static GObject * +g_fen_directory_monitor_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GObject *obj; + GFenDirectoryMonitorClass *klass; + GObjectClass *parent_class; + GFenDirectoryMonitor *self; + const gchar *dirname = NULL; + + klass = G_FEN_DIRECTORY_MONITOR_CLASS (g_type_class_peek (G_TYPE_FEN_DIRECTORY_MONITOR)); + parent_class = g_fen_directory_monitor_parent_class; + obj = parent_class->constructor (type, + n_construct_properties, + construct_properties); + + self = G_FEN_DIRECTORY_MONITOR (obj); + + dirname = G_LOCAL_DIRECTORY_MONITOR (self)->dirname; + g_assert (dirname != NULL); + + /* Will never fail as is_supported() should be called before instanciating + * anyway */ + if (!fen_init ()) + g_assert_not_reached (); + + /* FIXME: what to do about errors here? we can't return NULL or another + * kind of error and an assertion is probably too hard */ + self->sub = fen_sub_new (self, TRUE); + g_assert (self->sub); + + fen_add (dirname, self->sub, TRUE); + + return obj; +} + +static gboolean +g_fen_directory_monitor_is_supported (void) +{ + return fen_init (); +} + +static void +g_fen_directory_monitor_class_init (GFenDirectoryMonitorClass* klass) +{ + GObjectClass* gobject_class = G_OBJECT_CLASS (klass); + GFileMonitorClass *directory_monitor_class = G_FILE_MONITOR_CLASS (klass); + GLocalDirectoryMonitorClass *local_directory_monitor_class = G_LOCAL_DIRECTORY_MONITOR_CLASS (klass); + + gobject_class->finalize = g_fen_directory_monitor_finalize; + gobject_class->constructor = g_fen_directory_monitor_constructor; + directory_monitor_class->cancel = g_fen_directory_monitor_cancel; + + local_directory_monitor_class->mount_notify = TRUE; + local_directory_monitor_class->is_supported = g_fen_directory_monitor_is_supported; +} + +static void +g_fen_directory_monitor_init (GFenDirectoryMonitor* monitor) +{ +} + +static gboolean +g_fen_directory_monitor_cancel (GFileMonitor* monitor) +{ + GFenDirectoryMonitor *self = G_FEN_DIRECTORY_MONITOR (monitor); + + if (self->sub) { + fen_remove (G_LOCAL_DIRECTORY_MONITOR (self)->dirname, self->sub, TRUE); + fen_sub_delete (self->sub); + self->sub = NULL; + } + + if (G_FILE_MONITOR_CLASS (g_fen_directory_monitor_parent_class)->cancel) + (*G_FILE_MONITOR_CLASS (g_fen_directory_monitor_parent_class)->cancel) (monitor); + + return TRUE; +} + diff --git a/gio/fen/gfendirectorymonitor.h b/gio/fen/gfendirectorymonitor.h new file mode 100644 index 0000000..c22998c --- /dev/null +++ b/gio/fen/gfendirectorymonitor.h @@ -0,0 +1,55 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim:set expandtab ts=4 shiftwidth=4: */ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2006-2007 Red Hat, Inc. + * Copyright (C) 2007 Sebastian Dröge. + * + * 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: Alexander Larsson + * John McCutchan + * Sebastian Dröge + */ + +#ifndef __G_FEN_DIRECTORY_MONITOR_H__ +#define __G_FEN_DIRECTORY_MONITOR_H__ + +#include +#include +#include "glocaldirectorymonitor.h" +#include "giomodule.h" + +G_BEGIN_DECLS + +#define G_TYPE_FEN_DIRECTORY_MONITOR (_g_fen_directory_monitor_get_type ()) +#define G_FEN_DIRECTORY_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_FEN_DIRECTORY_MONITOR, GFenDirectoryMonitor)) +#define G_FEN_DIRECTORY_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), G_TYPE_FEN_DIRECTORY_MONITOR, GFenDirectoryMonitorClass)) +#define G_IS_FEN_DIRECTORY_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_FEN_DIRECTORY_MONITOR)) +#define G_IS_FEN_DIRECTORY_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_FEN_DIRECTORY_MONITOR)) + +typedef struct _GFenDirectoryMonitor GFenDirectoryMonitor; +typedef struct _GFenDirectoryMonitorClass GFenDirectoryMonitorClass; + +struct _GFenDirectoryMonitorClass { + GLocalDirectoryMonitorClass parent_class; +}; + +GType _g_fen_directory_monitor_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* __G_FEN_DIRECTORY_MONITOR_H__ */ diff --git a/gio/fen/gfenfilemonitor.c b/gio/fen/gfenfilemonitor.c new file mode 100644 index 0000000..0a977de --- /dev/null +++ b/gio/fen/gfenfilemonitor.c @@ -0,0 +1,148 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim:set expandtab ts=4 shiftwidth=4: */ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2006-2007 Red Hat, Inc. + * Copyright (C) 2007 Sebastian Dröge. + * 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: Alexander Larsson + * John McCutchan + * Sebastian Dröge + * Lin Ma + */ + +#include + +#include "gfenfilemonitor.h" +#include + +#include "fen-helper.h" + +#include "gioalias.h" + +struct _GFenFileMonitor +{ + GLocalFileMonitor parent_instance; + fen_sub* sub; +}; + +static gboolean g_fen_file_monitor_cancel (GFileMonitor* monitor); + +#define g_fen_file_monitor_get_type _g_fen_file_monitor_get_type +G_DEFINE_TYPE_WITH_CODE (GFenFileMonitor, g_fen_file_monitor, G_TYPE_LOCAL_FILE_MONITOR, + g_io_extension_point_implement (G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME, + g_define_type_id, + "fen", + 20)) + +static void +g_fen_file_monitor_finalize (GObject *object) +{ + GFenFileMonitor *self = G_FEN_FILE_MONITOR (object); + + if (self->sub) { + fen_remove (G_LOCAL_FILE_MONITOR (self)->filename, self->sub, FALSE); + fen_sub_delete (self->sub); + self->sub = NULL; + } + + if (G_OBJECT_CLASS (g_fen_file_monitor_parent_class)->finalize) + (*G_OBJECT_CLASS (g_fen_file_monitor_parent_class)->finalize) (object); +} + +static GObject * +g_fen_file_monitor_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GObject *obj; + GFenFileMonitorClass *klass; + GObjectClass *parent_class; + GFenFileMonitor *self; + const gchar *filename = NULL; + + klass = G_FEN_FILE_MONITOR_CLASS (g_type_class_peek (G_TYPE_FEN_FILE_MONITOR)); + parent_class = g_fen_file_monitor_parent_class; + obj = parent_class->constructor (type, + n_construct_properties, + construct_properties); + + self = G_FEN_FILE_MONITOR (obj); + + filename = G_LOCAL_FILE_MONITOR (obj)->filename; + + g_assert (filename != NULL); + + /* Will never fail as is_supported() should be called before instanciating + * anyway */ + if (!fen_init ()) + g_assert_not_reached (); + + /* FIXME: what to do about errors here? we can't return NULL or another + * kind of error and an assertion is probably too hard */ + self->sub = fen_sub_new (self, FALSE); + g_assert (self->sub); + + fen_add (filename, self->sub, FALSE); + + return obj; +} + +static gboolean +g_fen_file_monitor_is_supported (void) +{ + return fen_init (); +} + +static void +g_fen_file_monitor_class_init (GFenFileMonitorClass* klass) +{ + GObjectClass* gobject_class = G_OBJECT_CLASS (klass); + GFileMonitorClass *file_monitor_class = G_FILE_MONITOR_CLASS (klass); + GLocalFileMonitorClass *local_file_monitor_class = G_LOCAL_FILE_MONITOR_CLASS (klass); + + gobject_class->finalize = g_fen_file_monitor_finalize; + gobject_class->constructor = g_fen_file_monitor_constructor; + file_monitor_class->cancel = g_fen_file_monitor_cancel; + + local_file_monitor_class->is_supported = g_fen_file_monitor_is_supported; +} + +static void +g_fen_file_monitor_init (GFenFileMonitor* monitor) +{ +} + +static gboolean +g_fen_file_monitor_cancel (GFileMonitor* monitor) +{ + GFenFileMonitor *self = G_FEN_FILE_MONITOR (monitor); + + if (self->sub) { + fen_remove (G_LOCAL_FILE_MONITOR (self)->filename, self->sub, FALSE); + fen_sub_delete (self->sub); + self->sub = NULL; + } + + if (G_FILE_MONITOR_CLASS (g_fen_file_monitor_parent_class)->cancel) + (*G_FILE_MONITOR_CLASS (g_fen_file_monitor_parent_class)->cancel) (monitor); + + return TRUE; +} + diff --git a/gio/fen/gfenfilemonitor.h b/gio/fen/gfenfilemonitor.h new file mode 100644 index 0000000..e8b2e6a --- /dev/null +++ b/gio/fen/gfenfilemonitor.h @@ -0,0 +1,56 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim:set expandtab ts=4 shiftwidth=4: */ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2006-2007 Red Hat, Inc. + * Copyright (C) 2007 Sebastian Dröge. + * + * 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: Alexander Larsson + * John McCutchan + * Sebastian Dröge + */ + +#ifndef __G_FEN_FILE_MONITOR_H__ +#define __G_FEN_FILE_MONITOR_H__ + +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define G_TYPE_FEN_FILE_MONITOR (_g_fen_file_monitor_get_type ()) +#define G_FEN_FILE_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_FEN_FILE_MONITOR, GFenFileMonitor)) +#define G_FEN_FILE_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), G_TYPE_FEN_FILE_MONITOR, GFenFileMonitorClass)) +#define G_IS_FEN_FILE_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_FEN_FILE_MONITOR)) +#define G_IS_FEN_FILE_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_FEN_FILE_MONITOR)) + +typedef struct _GFenFileMonitor GFenFileMonitor; +typedef struct _GFenFileMonitorClass GFenFileMonitorClass; + +struct _GFenFileMonitorClass { + GLocalFileMonitorClass parent_class; +}; + +GType _g_fen_file_monitor_get_type (void); + +G_END_DECLS + +#endif /* __G_FEN_FILE_MONITOR_H__ */ diff --git a/gio/giomodule.c b/gio/giomodule.c index 4aa32c3..85fdd6c 100644 --- a/gio/giomodule.c +++ b/gio/giomodule.c @@ -285,6 +285,8 @@ g_io_modules_load_all_in_directory (const char *dirname) G_LOCK_DEFINE_STATIC (loaded_dirs); +extern GType _g_fen_directory_monitor_get_type (void); +extern GType _g_fen_file_monitor_get_type (void); extern GType _g_inotify_directory_monitor_get_type (void); extern GType _g_inotify_file_monitor_get_type (void); extern GType _g_unix_volume_monitor_get_type (void); @@ -331,9 +333,9 @@ _g_io_modules_ensure_loaded (void) #if defined(HAVE_SYS_INOTIFY_H) || defined(HAVE_LINUX_INOTIFY_H) _g_inotify_directory_monitor_get_type (); _g_inotify_file_monitor_get_type (); -#endif -#ifdef G_OS_WIN32 - g_win32_directory_monitor_get_type (); +#elif defined(HAVE_FEN) + _g_fen_directory_monitor_get_type (); + _g_fen_file_monitor_get_type (); #endif #ifdef G_OS_UNIX _g_unix_volume_monitor_get_type (); -- 2.7.4