mirror of
				https://gitlab.gnome.org/GNOME/glib.git
				synced 2025-10-31 16:32:18 +01:00 
			
		
		
		
	fen: remove Solaris file monitor support
This code is unmaintained and we have no way to port it to the new file monitoring API.
This commit is contained in:
		
							
								
								
									
										17
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								configure.ac
									
									
									
									
									
								
							| @@ -1677,22 +1677,6 @@ AC_CHECK_HEADERS([sys/event.h], | ||||
|  | ||||
| AM_CONDITIONAL(HAVE_KQUEUE, [test "$kqueue_support" = "yes"]) | ||||
|  | ||||
| dnl ********************************* | ||||
| dnl ** Check for Solaris FEN (GIO) ** | ||||
| dnl ********************************* | ||||
| fen_support=no | ||||
| AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ | ||||
| #include <port.h>  | ||||
| #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 **************************** | ||||
| @@ -3616,7 +3600,6 @@ gio/gnetworking.h | ||||
| gio/xdgmime/Makefile | ||||
| gio/inotify/Makefile | ||||
| gio/kqueue/Makefile | ||||
| gio/fen/Makefile | ||||
| gio/fam/Makefile | ||||
| gio/win32/Makefile | ||||
| gio/tests/Makefile | ||||
|   | ||||
| @@ -227,13 +227,6 @@ platform_libadd += kqueue/libkqueue.la | ||||
| platform_deps += kqueue/libkqueue.la | ||||
| endif | ||||
|  | ||||
| if HAVE_FEN | ||||
| AM_CPPFLAGS += -DHAVE_FEN | ||||
| SUBDIRS += fen | ||||
| platform_libadd += fen/libfen.la | ||||
| platform_deps += fen/libfen.la | ||||
| endif | ||||
|  | ||||
| if OS_WIN32 | ||||
| SUBDIRS += win32 | ||||
| platform_libadd += win32/libgiowin32.la | ||||
|   | ||||
| @@ -1,27 +0,0 @@ | ||||
| include $(top_srcdir)/glib.mk | ||||
|  | ||||
| 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-helper.c 		\ | ||||
| 	fen-helper.h		\ | ||||
| 	gfenfilemonitor.c		\ | ||||
| 	gfenfilemonitor.h		\ | ||||
| 	gfendirectorymonitor.c	\ | ||||
| 	gfendirectorymonitor.h	\ | ||||
| 	$(NULL) | ||||
|  | ||||
| libfen_la_CFLAGS = \ | ||||
| 	$(GLIB_HIDDEN_VISIBILITY_CFLAGS)	\ | ||||
| 	-DG_LOG_DOMAIN=\"GLib-GIO\"	\ | ||||
| 	$(gio_INCLUDES) 			\ | ||||
| 	$(GLIB_DEBUG_FLAGS)		\ | ||||
| 	-DGIO_MODULE_DIR=\"$(GIO_MODULE_DIR)\"	\ | ||||
| 	-DGIO_COMPILATION		\ | ||||
| 	-DG_DISABLE_DEPRECATED | ||||
| @@ -1,74 +0,0 @@ | ||||
| /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ | ||||
| /* vim:set expandtab ts=4 shiftwidth=4: */ | ||||
| /*  | ||||
|  * Copyright (c) 2008, 2010 Oracle and/or its affiliates, Inc. All rights | ||||
|  * reserved. | ||||
|  * | ||||
|  * 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, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  * Authors: Lin Ma <lin.ma@sun.com> | ||||
|  */ | ||||
|  | ||||
| #include "config.h" | ||||
| #include <glib.h> | ||||
| #include <glib/gprintf.h> | ||||
| #include "fen-node.h" | ||||
| #include "fen-dump.h" | ||||
|  | ||||
| G_LOCK_EXTERN (fen_lock); | ||||
|  | ||||
| /*-------------------- node ------------------*/ | ||||
| static void | ||||
| dump_node (node_t* node, gpointer data) | ||||
| { | ||||
|     g_printf ("n:0x%p ds:0x%p s:0x%p %s\n", node, node->dir_subs, node->subs, NODE_NAME(node)); | ||||
| } | ||||
|  | ||||
| static void | ||||
| dump_tree (node_t* node) | ||||
| { | ||||
|     if (G_TRYLOCK (fen_lock)) { | ||||
|         node_traverse(NULL, dump_node, NULL); | ||||
|         G_UNLOCK (fen_lock); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* ------------------ fdata port hash --------------------*/ | ||||
| void | ||||
| dump_hash_cb (gpointer key, | ||||
|   gpointer value, | ||||
|   gpointer user_data) | ||||
| { | ||||
|     g_printf ("k:0x%p v:0x%p >\n", 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 (node_event_t* ev, gpointer user_data) | ||||
| { | ||||
|     node_t* node = ev->user_data; | ||||
|     g_printf ("ne:0x%p e:%p n:0x%p ds:0x%p s:0x%p s\n", ev, ev->e, node, node->dir_subs, node->subs, NODE_NAME(node)); | ||||
| } | ||||
| @@ -1,27 +0,0 @@ | ||||
| /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ | ||||
| /* vim:set expandtab ts=4 shiftwidth=4: */ | ||||
| /*  | ||||
|  * Copyright (c) 2008, 2010 Oracle and/or its affiliates, Inc. All rights | ||||
|  * reserved. | ||||
|  * | ||||
|  * 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, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  * Authors: Lin Ma <lin.ma@sun.com> | ||||
|  */ | ||||
|  | ||||
| #ifndef _FEN_DUMP_H_ | ||||
| #define _FEN_DUMP_H_ | ||||
|  | ||||
|  | ||||
| #endif /* _FEN_DUMP_H_ */ | ||||
| @@ -1,193 +0,0 @@ | ||||
| /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ | ||||
| /* vim:set expandtab ts=4 shiftwidth=4: */ | ||||
| /*  | ||||
|  * Copyright (c) 2008, 2010 Oracle and/or its affiliates, Inc. All rights | ||||
|  * reserved. | ||||
|  * | ||||
|  * 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, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  * Authors: Lin Ma <lin.ma@sun.com> | ||||
|  */ | ||||
|  | ||||
| #include "config.h" | ||||
| #include <glib.h> | ||||
| #include "fen-helper.h" | ||||
| #include "fen-kernel.h" | ||||
| #ifdef GIO_COMPILATION | ||||
| #include <gio/gfilemonitor.h> | ||||
| #else | ||||
| #include "gam_event.h" | ||||
| #include "gam_server.h" | ||||
| #include "gam_protocol.h" | ||||
| #endif | ||||
|  | ||||
| #ifdef GIO_COMPILATION | ||||
| #define FH_W if (FALSE) g_debug | ||||
| #else | ||||
| #include "gam_error.h" | ||||
| #define FH_W(...) GAM_DEBUG(DEBUG_INFO, __VA_ARGS__) | ||||
| #endif | ||||
|  | ||||
| G_LOCK_EXTERN (fen_lock); | ||||
|  | ||||
| /* misc */ | ||||
| static void | ||||
| scan_children_init(node_t *f, gpointer sub) | ||||
| { | ||||
|     gboolean emit; | ||||
|     gint event; | ||||
|  | ||||
|     FH_W ("%s %s [0x%p]\n", __func__, NODE_NAME(f), f); | ||||
|  | ||||
| #ifdef GIO_COMPILATION | ||||
|     emit = FALSE; | ||||
|     event = G_FILE_MONITOR_EVENT_CREATED; | ||||
| #else | ||||
|     emit = TRUE; | ||||
|     event = GAMIN_EVENT_EXISTS; | ||||
| #endif | ||||
|  | ||||
|     if (!NODE_HAS_FLAG(f, NODE_FLAG_SNAPSHOT_UPDATED)) { | ||||
|         /* TODO snapshot should also compare to the sub created timestamp. */ | ||||
|         /* GIO initially doesn't emit created/existed events. */ | ||||
|         node_create_children_snapshot(f, event, emit); | ||||
|     } else { | ||||
|         GHashTableIter iter; | ||||
|         gpointer value; | ||||
|  | ||||
|         g_hash_table_iter_init (&iter, f->children); | ||||
|         while (g_hash_table_iter_next (&iter, NULL, &value)) { | ||||
|             node_t *child = (node_t *)value; | ||||
|  | ||||
| #ifdef GIO_COMPILATION | ||||
|             /* GIO initially doesn't emit created/existed events. */ | ||||
|             /* g_file_monitor_emit_event(G_FILE_MONITOR(sub), child->gfile, NULL, event); */ | ||||
| #else | ||||
|             gam_server_emit_one_event(NODE_NAME(child), gam_subscription_is_dir(sub), event, sub, 1); | ||||
| #endif | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * fen_add | ||||
|  *  | ||||
|  * Won't hold a ref, we have a timout callback to clean unused node_t. | ||||
|  * 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_t* f; | ||||
|  | ||||
|     g_assert (filename); | ||||
|     g_assert (sub); | ||||
|  | ||||
|     G_LOCK (fen_lock); | ||||
| 	f = node_find(NULL, filename, TRUE); | ||||
|     FH_W ("%s 0x%p sub[0x%p] %s\n", __func__, f, sub, filename); | ||||
|     g_assert (f); | ||||
|  | ||||
|     /* Update timestamp, the events in global queue will compare itself to this | ||||
|      * timestamp to decide if be emitted. TODO, timestamp should be per sub. | ||||
|      */ | ||||
|     if (!NODE_IS_ACTIVE(f)) { | ||||
|         g_get_current_time(&f->atv); | ||||
|     } | ||||
|  | ||||
|     if (is_mondir) { | ||||
|         f->dir_subs = g_list_prepend(f->dir_subs, sub); | ||||
|     } else { | ||||
|         f->subs = g_list_prepend(f->subs, sub); | ||||
|     } | ||||
|      | ||||
|     if (NODE_HAS_STATE(f, NODE_STATE_ASSOCIATED) || | ||||
|       (node_lstat(f) == 0 && port_add(f) == 0)) { | ||||
| #ifndef GIO_COMPILATION | ||||
|         gam_server_emit_one_event (NODE_NAME(f), | ||||
|           gam_subscription_is_dir (sub), GAMIN_EVENT_EXISTS, sub, 1); | ||||
| #endif | ||||
|         if (is_mondir) { | ||||
|             scan_children_init (f, sub); | ||||
|         } | ||||
|     } else { | ||||
| #ifndef GIO_COMPILATION | ||||
|         gam_server_emit_one_event (NODE_NAME(f), | ||||
|           gam_subscription_is_dir (sub), GAMIN_EVENT_DELETED, sub, 1); | ||||
| #endif | ||||
|         node_adjust_deleted (f); | ||||
|     } | ||||
| #ifndef GIO_COMPILATION | ||||
|     gam_server_emit_one_event (NODE_NAME(f), | ||||
|       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_t* f; | ||||
|      | ||||
|     g_assert (filename); | ||||
|     g_assert (sub); | ||||
|  | ||||
|     G_LOCK (fen_lock); | ||||
| 	f = node_find(NULL, filename, FALSE); | ||||
|     FH_W ("%s 0x%p sub[0x%p] %s\n", __func__, f, sub, filename); | ||||
|  | ||||
|     if (f) { | ||||
|         if (is_mondir) { | ||||
|             f->dir_subs = g_list_remove(f->dir_subs, sub); | ||||
|         } else { | ||||
|             f->subs = g_list_remove(f->subs, sub); | ||||
|         } | ||||
|  | ||||
|         if (!NODE_IS_ACTIVE(f)) { | ||||
|             node_try_delete (f); | ||||
|         } | ||||
|     } | ||||
|     G_UNLOCK (fen_lock); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * fen_init: | ||||
|  *  | ||||
|  * FEN subsystem initializing. | ||||
|  */ | ||||
| gboolean | ||||
| fen_init () | ||||
| { | ||||
|     static gboolean initialized = FALSE; | ||||
|     static gboolean result = FALSE; | ||||
|  | ||||
|     G_LOCK (fen_lock); | ||||
|     if (initialized) { | ||||
|         G_UNLOCK (fen_lock); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     result = node_class_init(); | ||||
|  | ||||
|     if (!result) { | ||||
|         G_UNLOCK (fen_lock); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     initialized = TRUE; | ||||
|  | ||||
|     G_UNLOCK (fen_lock); | ||||
|     return result; | ||||
| } | ||||
| @@ -1,31 +0,0 @@ | ||||
| /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ | ||||
| /* vim:set expandtab ts=4 shiftwidth=4: */ | ||||
| /*  | ||||
|  * Copyright (c) 2008, 2010 Oracle and/or its affiliates, Inc. All rights | ||||
|  * reserved. | ||||
|  * | ||||
|  * 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, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  * Authors: Lin Ma <lin.ma@sun.com> | ||||
|  */ | ||||
|  | ||||
| #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); | ||||
|  | ||||
| gboolean fen_init (); | ||||
|  | ||||
| #endif /* _FEN_HELPER_H_ */ | ||||
| @@ -1,550 +0,0 @@ | ||||
| /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ | ||||
| /* vim:set expandtab ts=4 shiftwidth=4: */ | ||||
| /*  | ||||
|  * Copyright (c) 2008, 2010 Oracle and/or its affiliates, Inc. All rights | ||||
|  * reserved. | ||||
|  * | ||||
|  * 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, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  * Authors: Lin Ma <lin.ma@sun.com> | ||||
|  */ | ||||
|  | ||||
| #include "config.h" | ||||
| #include <rctl.h> | ||||
| #include <strings.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <unistd.h> | ||||
| #include <glib.h> | ||||
| #include "fen-kernel.h" | ||||
| #include "fen-dump.h" | ||||
|  | ||||
| #ifdef GIO_COMPILATION | ||||
| #define FK_W if (FALSE) g_debug | ||||
| #else | ||||
| #include "gam_error.h" | ||||
| #define FK_W(...) GAM_DEBUG(DEBUG_INFO, __VA_ARGS__) | ||||
| #endif | ||||
|  | ||||
| G_LOCK_DEFINE (fen_lock); | ||||
|  | ||||
| static ulong max_port_events = 512; | ||||
| static GList *pn_visible_list;	/* the queue of ports which don't have the max objs */ | ||||
| static GQueue *g_eventq = NULL; | ||||
| static timespec_t zero_wait; | ||||
| static void (*user_process_events_cb) (gpointer, node_event_t*); | ||||
| static port_event_t *pevents = NULL; | ||||
| static gint PE_ALLOC = 2048; | ||||
| static GHashTable *renamed_hash = NULL; /* <parent node, ev> */ | ||||
|  | ||||
| typedef struct _PSource { | ||||
|     GSource  source;            /* Inherit from GSource, must be the first. */ | ||||
|     GPollFD  gfd; | ||||
|     gboolean pending; | ||||
|     uint_t   event_growing_factor; | ||||
|     uint_t   pending_events; | ||||
| } PSource; | ||||
|  | ||||
| #define PGPFD(s)             (&((PSource *)(s))->gfd) | ||||
| #define SLEEP_BASE_TIME      10	/* in milliseconds */ | ||||
| #define EXPECT_INC_EVENTS(pn)  (1 << (pn->event_growing_factor)) | ||||
|  | ||||
| #define RENAME_EVENTS_INTERVAL 500 /* in milliseconds */ | ||||
| #define PROCESS_PORT_EVENTS_TIME 1000 /* in milliseconds */ | ||||
| guint process_port_event_id = 0; | ||||
|  | ||||
| static gchar* _event_strings(int event); | ||||
| static const gchar* _event_string (int event); | ||||
| static GSource *psource_new(); | ||||
|  | ||||
| static gboolean port_prepare(GSource *source, gint *timeout_); | ||||
| static gboolean port_check(GSource *source); | ||||
| static gboolean port_dispatch(GSource *source, GSourceFunc callback, gpointer user_data); | ||||
| static GSourceFuncs fen_source_func = { | ||||
|     port_prepare, | ||||
|     port_check, | ||||
|     port_dispatch, | ||||
|     NULL | ||||
| }; | ||||
|  | ||||
| static gboolean | ||||
| port_prepare(GSource *source, gint *timeout_) | ||||
| { | ||||
|     return FALSE; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| port_check(GSource *source) | ||||
| { | ||||
| 	PSource *pn = (PSource *)source; | ||||
|     uint_t nget; | ||||
|      | ||||
|     if (pn->pending) { | ||||
|         pn->pending = FALSE; | ||||
|         g_source_add_poll(source, PGPFD(source)); | ||||
|         g_source_unref(source); | ||||
|         return FALSE; | ||||
|     } | ||||
|  | ||||
|     if (!(PGPFD(pn)->revents & G_IO_IN)) | ||||
|         return FALSE; | ||||
|  | ||||
|     if (port_getn(PGPFD(source)->fd, NULL, 0, &nget, 0) == 0) { | ||||
|         if (nget - pn->pending_events > EXPECT_INC_EVENTS(pn)) { | ||||
|             /* Sleep for a while. */ | ||||
|             pn->pending_events = nget; | ||||
|             pn->event_growing_factor ++; | ||||
|  | ||||
|             pn->pending = TRUE; | ||||
|             g_source_ref(source); | ||||
|             g_source_remove_poll(source, PGPFD(source)); | ||||
|             g_timeout_add(SLEEP_BASE_TIME, | ||||
|               (GSourceFunc)port_check, | ||||
|               (gpointer)pn); | ||||
|             return FALSE; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pn->pending_events = 0; | ||||
|     pn->event_growing_factor = 0; | ||||
|  | ||||
|     return TRUE; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| port_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) | ||||
| { | ||||
|     node_t *f; | ||||
| 	uint_t nget = 0; | ||||
| 	uint_t total = 0; | ||||
|  | ||||
|     FK_W ("%s 0x%p fd %d\n", __func__, source, PGPFD(source)->fd); | ||||
|  | ||||
|     G_LOCK (fen_lock); | ||||
|     do { | ||||
|         nget = 1; | ||||
|         if (port_getn(PGPFD(source)->fd, pevents, PE_ALLOC, &nget, &zero_wait) == 0) { | ||||
|             int i; | ||||
|             for (i = 0; i < nget; i++) { | ||||
|                 f = (node_t *)pevents[i].portev_user; | ||||
|  | ||||
|                 if (pevents[i].portev_source == PORT_SOURCE_FILE) { | ||||
|  | ||||
|                     NODE_CLE_STATE(f, NODE_STATE_ASSOCIATED); | ||||
|                     NODE_SET_STATE(f, NODE_STATE_HAS_EVENTS); | ||||
|  | ||||
|                     if (HAS_NO_EXCEPTION_EVENTS(pevents[i].portev_events)) { | ||||
|                         /* If the events do not show it's deleted, update | ||||
|                          * file timestamp to avoid missing events next time. | ||||
|                          */ | ||||
|                         if (node_lstat(f) != 0 /* || port_add(f) != 0 */) { | ||||
|                             /* Included deleted event. */ | ||||
|                             pevents[i].portev_events |= FILE_DELETE; | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     /* Queue it and waiting for processing. */ | ||||
|                     g_queue_push_tail(g_eventq, | ||||
|                       node_event_new(pevents[i].portev_events, (gpointer)f)); | ||||
|  | ||||
|                 } else { | ||||
|                     FK_W ("[kernel] unknown portev_source %d\n", pevents[i].portev_source); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             total += nget; | ||||
|  | ||||
|         } else { | ||||
|             FK_W ("[kernel] port_getn %s\n", g_strerror (errno)); | ||||
|             break; | ||||
|         } | ||||
|     } while (nget == PE_ALLOC); | ||||
|  | ||||
|     G_UNLOCK (fen_lock); | ||||
|  | ||||
|     if (total > 0 && callback) { | ||||
|         FK_W ("[kernel] get total %ld events\n", total); | ||||
|         return callback (user_data); | ||||
|     } | ||||
|     return TRUE; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| process_renamed_hash_cb(gpointer key, gpointer value, gpointer user_data) | ||||
| { | ||||
|     node_event_t *ev = value; | ||||
|  | ||||
| #if 0 | ||||
|     node_add_event(ev->user_data, ev); | ||||
| #else | ||||
|     user_process_events_cb(ev->user_data, ev); | ||||
| #endif | ||||
|     /* Always delete self from hash. */ | ||||
|     return TRUE; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| port_events_process_cb(gpointer user_data) | ||||
| { | ||||
|     node_event_t *ev; | ||||
|      | ||||
|     G_LOCK (fen_lock); | ||||
|  | ||||
| 	/* Processing g_eventq */ | ||||
|     while ((ev = (node_event_t*)g_queue_pop_head (g_eventq)) != NULL) { | ||||
|  | ||||
|         /* FK_W ("[%s] 0x%p %s\n", __func__, ev, _event_string (ev->e)); */ | ||||
|  | ||||
|         { | ||||
|             gchar *log = _event_strings(ev->e); | ||||
|             FK_W ("%s %s %s\n", __func__, NODE_NAME(ev->user_data), log); | ||||
|             g_free(log); | ||||
|         } | ||||
|  | ||||
| #ifdef GIO_COMPILATION | ||||
|         /* Use the parent node as a hash, because only the dir_subs in the | ||||
|          * parent node should receive MOVE event. | ||||
|          */ | ||||
|         if (NODE_PARENT(ev->user_data)) { | ||||
|             if (ev->e == FILE_RENAME_TO) { | ||||
|                 g_hash_table_insert(renamed_hash, NODE_PARENT(ev->user_data), ev); | ||||
|                 g_time_val_add(&ev->rename_tv, RENAME_EVENTS_INTERVAL); | ||||
|                 continue; | ||||
|             } | ||||
|             if (ev->e == FILE_RENAME_FROM) { | ||||
|                 node_event_t *pair_ev; | ||||
|  | ||||
|                 pair_ev = g_hash_table_lookup(renamed_hash, NODE_PARENT(ev->user_data)); | ||||
|                 if (pair_ev && node_timeval_lt(&ev->ctv, &pair_ev->rename_tv)) { | ||||
|                     g_hash_table_remove(renamed_hash, NODE_PARENT(ev->user_data)); | ||||
|                     pair_ev->pair_data = ev->user_data; | ||||
|                     /* Free ev, exchange pair_ev and ev. */ | ||||
|                     node_event_delete(ev); | ||||
|                     ev = pair_ev; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| #endif | ||||
|      | ||||
| #if 0 | ||||
|         node_add_event(ev->user_data, ev); | ||||
| #else | ||||
|         user_process_events_cb(ev->user_data, ev); | ||||
| #endif | ||||
|     } | ||||
|  | ||||
|     /* Processing the events in renamed_hash. TODO we should delay it and wait | ||||
|      * for more possible pair. | ||||
|      */ | ||||
|     g_hash_table_foreach_remove(renamed_hash, process_renamed_hash_cb, NULL); | ||||
|  | ||||
|     G_UNLOCK (fen_lock); | ||||
|  | ||||
|     process_port_event_id = 0; | ||||
|     return FALSE; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| port_events_read_cb(gpointer user_data) | ||||
| { | ||||
|  | ||||
|     if (process_port_event_id == 0) { | ||||
|         process_port_event_id = g_timeout_add(PROCESS_PORT_EVENTS_TIME, | ||||
|           port_events_process_cb, | ||||
|           NULL); | ||||
|     } | ||||
|  | ||||
| 	return TRUE; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * malloc PSource and port_create, start thread at pnode_ref. | ||||
|  * if psource_new succeeded, the PSource will never | ||||
|  * be freed. So PSource can be freed only in psource_new. | ||||
|  * Note pnode_monitor_remove_all can also free PSource, but currently no one | ||||
|  * invork it. | ||||
|  */ | ||||
| static GSource* | ||||
| psource_new() | ||||
| { | ||||
|     GSource *source = NULL; | ||||
|     int fd; | ||||
|  | ||||
|     if ((fd = port_create()) >= 0) { | ||||
|         source = g_source_new(&fen_source_func, sizeof(PSource)); | ||||
|         PGPFD(source)->fd = fd; | ||||
|         PGPFD(source)->events = G_IO_IN | G_IO_HUP | G_IO_ERR; | ||||
|         g_source_set_callback(source, port_events_read_cb, NULL, NULL); | ||||
|         g_source_attach(source, NULL); | ||||
|         g_source_unref(source); | ||||
|         g_source_add_poll(source, PGPFD(source)); | ||||
|  | ||||
|         FK_W ("%s 0x%p fd %d\n", __func__, source, PGPFD(source)->fd); | ||||
|     } else { | ||||
|         FK_W ("PORT_CREATE %s\n", g_strerror(errno)); | ||||
|         g_return_val_if_reached(NULL); | ||||
|     } | ||||
|  | ||||
| 	return source; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * port_add: | ||||
|  * | ||||
|  * Unsafe, need lock fen_lock. | ||||
|  * port_add will associate a GSource to @f->source | ||||
|  */ | ||||
| gint | ||||
| port_add(node_t *f) | ||||
| { | ||||
| 	GSource *source = f->source; | ||||
|  | ||||
|     FK_W ("%s [0x%p] %s\n", __func__, f, NODE_NAME(f)); | ||||
|  | ||||
|     g_assert(f); | ||||
|     g_assert(NODE_HAS_FLAG(f, NODE_FLAG_STAT_UPDATED)); | ||||
|  | ||||
|     /* if (!NODE_HAS_FLAG(f, NODE_FLAG_STAT_DONE)) { */ | ||||
|     /*     if (NODE_STAT(f) != 0) { */ | ||||
|     /*         return errno; */ | ||||
|     /*     } */ | ||||
|     /* } */ | ||||
|  | ||||
|     /* Try re-use f->pn. f->pn may be used by other request, e.g. f is deleted | ||||
|      * for a long time. So if pn is full, we try to open a new one. | ||||
|      */ | ||||
|     if (!source) { | ||||
| start_over: | ||||
|         /* Try the next visible source. */ | ||||
|         if (pn_visible_list) { | ||||
|             source = (GSource *)pn_visible_list->data; | ||||
|         } else { | ||||
|             if ((source = psource_new()) != NULL) { | ||||
|                 g_assert (g_list_find (pn_visible_list, source) == NULL); | ||||
|                 pn_visible_list = g_list_prepend (pn_visible_list, source); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (port_associate(PGPFD(source)->fd, PORT_SOURCE_FILE, (uintptr_t)FILE_OBJECT(f), | ||||
|         CONCERNED_EVENTS, | ||||
|         (void *)f) == 0) { | ||||
|         f->source = source; | ||||
|         NODE_SET_STATE(f, NODE_STATE_ASSOCIATED); | ||||
|         NODE_CLE_FLAG(f, NODE_FLAG_STAT_UPDATED); | ||||
|         FK_W ("PORT_ASSOCIATE 0x%p OK\n", f); | ||||
|         return 0; | ||||
|     } else if (errno == EAGAIN) { | ||||
|         /* Full, remove it. */ | ||||
|         pn_visible_list = g_list_remove (pn_visible_list, source); | ||||
|         /* Re-add to port */ | ||||
|         goto start_over; | ||||
|  | ||||
|     } else if (errno == ENOENT) { | ||||
|         /* File is not exist */ | ||||
|     } else if (errno == ENOTSUP) { | ||||
|         /* FS is not supported. Currently we think it no longer make sense to | ||||
|          * monitor it, so clean the stat info and return 0 to ignore this | ||||
|          * node. If there are requirement, we can consider to add polling | ||||
|          * method. | ||||
|          */ | ||||
|         NODE_CLE_FLAG(f, NODE_FLAG_STAT_UPDATED); | ||||
|         return 0; | ||||
|     } else { | ||||
|         FK_W ("PORT_ASSOCIATE 0x%p %s\n", f, g_strerror (errno)); | ||||
|     } | ||||
|  | ||||
|     /* No matter if associated successfully, stat info is out-of-date, so clean it. */ | ||||
|     NODE_CLE_FLAG(f, NODE_FLAG_STAT_UPDATED); | ||||
|     return errno; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * port_remove: | ||||
|  * | ||||
|  * < private > | ||||
|  * Unsafe, need lock fen_lock. | ||||
|  */ | ||||
| void | ||||
| port_remove (node_t *f) | ||||
| { | ||||
|     /* g_assert(f->source); */ | ||||
|  | ||||
|     if (NODE_HAS_STATE(f, NODE_STATE_ASSOCIATED)) { | ||||
|         /* Mark unregisted. */ | ||||
|         if (port_dissociate(PGPFD(f->source)->fd, PORT_SOURCE_FILE, (uintptr_t)FILE_OBJECT(f)) == 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. | ||||
|              */ | ||||
|             NODE_CLE_STATE(f, NODE_STATE_ASSOCIATED); | ||||
|             FK_W ("PORT_DISSOCIATE 0x%p OK\n", f); | ||||
|         } else if (errno == ENOENT) { | ||||
|             /* The file has been removed from port, after port_get or before | ||||
|              * port_get but DELETED event has been generated. | ||||
|              * Ignored. */ | ||||
|         } else { | ||||
|             FK_W ("PORT_DISSOCIATE 0x%p %s\n", f, g_strerror (errno)); | ||||
|             g_return_if_reached(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Get Solaris resouce values. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| extern gboolean | ||||
| port_class_init (void (*user_process_events_callback) (gpointer, node_event_t*)) | ||||
| { | ||||
| 	rctlblk_t *rblk; | ||||
|  | ||||
| 	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 ("max_port_events = %u\n", max_port_events); | ||||
|         free (rblk); | ||||
| 	} | ||||
|     renamed_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, | ||||
|       NULL, NULL); | ||||
|     if (renamed_hash == NULL) { | ||||
| 		FK_W ("[kernel] FEN global renamed queue initializing faild\n"); | ||||
|         return FALSE; | ||||
|     } | ||||
|     if ((g_eventq = g_queue_new ()) == NULL) { | ||||
| 		FK_W ("[kernel] FEN global event queue initializing faild\n"); | ||||
|         return FALSE; | ||||
|     } | ||||
|     if (user_process_events_callback == NULL) { | ||||
| 		FK_W ("[kernel] FEN global no user_process_events_callback\n"); | ||||
|         return FALSE; | ||||
|     } | ||||
|     user_process_events_cb = user_process_events_callback; | ||||
|     memset (&zero_wait, 0, sizeof (timespec_t)); | ||||
|  | ||||
|     pevents = g_malloc(PE_ALLOC * sizeof(port_event_t)); | ||||
|     if (pevents == NULL) { | ||||
| 		FK_W ("[kernel] FEN global alloc pevents failed\n"); | ||||
|         return FALSE; | ||||
|     } | ||||
|  | ||||
| 	return TRUE; | ||||
| } | ||||
|  | ||||
| static gchar* | ||||
| printevent (const char *pname, int event, const char *tag) | ||||
| { | ||||
|     static gchar	*event_string = NULL; | ||||
|     GString			*str; | ||||
|  | ||||
|     g_free(event_string); | ||||
|  | ||||
|     str = g_string_new (""); | ||||
|     g_string_printf (str, "[%s] [%-20s]", tag, pname); | ||||
|     if (event & FILE_ACCESS) { | ||||
|         str = g_string_append (str, " ACCESS"); | ||||
|     } | ||||
|     if (event & FILE_MODIFIED) { | ||||
|         str = g_string_append (str, " MODIFIED"); | ||||
|     } | ||||
|     if (event & FILE_ATTRIB) { | ||||
|         str = g_string_append (str, " ATTRIB"); | ||||
|     } | ||||
|     if (event & FILE_DELETE) { | ||||
|         str = g_string_append (str, " DELETE"); | ||||
|     } | ||||
|     if (event & FILE_RENAME_TO) { | ||||
|         str = g_string_append (str, " RENAME_TO"); | ||||
|     } | ||||
|     if (event & FILE_RENAME_FROM) { | ||||
|         str = g_string_append (str, " RENAME_FROM"); | ||||
|     } | ||||
|     if (event & UNMOUNTED) { | ||||
|         str = g_string_append (str, " UNMOUNTED"); | ||||
|     } | ||||
|     if (event & MOUNTEDOVER) { | ||||
|         str = g_string_append (str, " MOUNTEDOVER"); | ||||
|     } | ||||
|     event_string = str->str; | ||||
|     g_string_free (str, FALSE); | ||||
|     return event_string; | ||||
| } | ||||
|  | ||||
| static gchar * | ||||
| _event_strings(int event) | ||||
| { | ||||
|     GString *str = g_string_sized_new(80); | ||||
|  | ||||
|     if (event & FILE_DELETE) | ||||
|         g_string_append(str, " FILE_DELETE"); | ||||
|  | ||||
|     if (event & FILE_RENAME_FROM) | ||||
|         g_string_append(str, " FILE_RENAME_FROM"); | ||||
|  | ||||
|     if (event & FILE_MODIFIED) | ||||
|         g_string_append(str, " FILE_MODIFIED"); | ||||
|  | ||||
|     if (event & FILE_RENAME_TO) | ||||
|         g_string_append(str, " FILE_RENAME_TO"); | ||||
|  | ||||
|     if (event & MOUNTEDOVER) | ||||
|         g_string_append(str, " MOUNTEDOVER"); | ||||
|  | ||||
|     if (event & FILE_ATTRIB) | ||||
|         g_string_append(str, " FILE_ATTRIB"); | ||||
|  | ||||
|     if (event & UNMOUNTED) | ||||
|         g_string_append(str, " UNMOUNTED"); | ||||
|  | ||||
|     if (event & FILE_ACCESS) | ||||
|         g_string_append(str, " FILE_ACCESS"); | ||||
|  | ||||
|     return g_string_free(str, FALSE); | ||||
| } | ||||
|  | ||||
| static 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"; | ||||
|     } | ||||
| } | ||||
| @@ -1,41 +0,0 @@ | ||||
| /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ | ||||
| /* vim:set expandtab ts=4 shiftwidth=4: */ | ||||
| /*  | ||||
|  * Copyright (c) 2008, 2010 Oracle and/or its affiliates, Inc. All rights | ||||
|  * reserved. | ||||
|  * | ||||
|  * 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, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  * Authors: Lin Ma <lin.ma@sun.com> | ||||
|  */ | ||||
|  | ||||
| #include <sys/types.h> | ||||
| #include <errno.h> | ||||
|  | ||||
| #include "fen-node.h" | ||||
|  | ||||
| #ifndef _FEN_KERNEL_H_ | ||||
| #define _FEN_KERNEL_H_ | ||||
|  | ||||
| #define CONCERNED_EVENTS (FILE_MODIFIED | FILE_ATTRIB | FILE_NOFOLLOW) | ||||
| #define EXCEPTION_EVENTS (FILE_DELETE | FILE_RENAME_FROM) | ||||
| #define HAS_EXCEPTION_EVENTS(e) ((e & EXCEPTION_EVENTS) != 0) | ||||
| #define HAS_NO_EXCEPTION_EVENTS(e) ((e & EXCEPTION_EVENTS) == 0) | ||||
|  | ||||
| gint port_add (node_t* f); | ||||
| void port_remove (node_t *f); | ||||
|  | ||||
| gboolean port_class_init (); | ||||
|  | ||||
| #endif /* _FEN_KERNEL_H_ */ | ||||
| @@ -1,638 +0,0 @@ | ||||
| /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ | ||||
| /* vim:set expandtab ts=4 shiftwidth=4: */ | ||||
| /*  | ||||
|  * Copyright (c) 2008, 2010 Oracle and/or its affiliates, Inc. All rights | ||||
|  * reserved. | ||||
|  * | ||||
|  * 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, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  * Authors: Lin Ma <lin.ma@sun.com> | ||||
|  */ | ||||
|  | ||||
| #include "config.h" | ||||
| #include <sys/stat.h> | ||||
| #include <errno.h> | ||||
| #include <strings.h> | ||||
| #include <glib.h> | ||||
| #include "fen-kernel.h" | ||||
| #include "fen-node.h" | ||||
| #include "fen-dump.h" | ||||
|  | ||||
| #ifdef GIO_COMPILATION | ||||
| #include <gio/gfilemonitor.h> | ||||
| #else | ||||
| #include "gam_event.h" | ||||
| #include "gam_server.h" | ||||
| #include "gam_protocol.h" | ||||
| #endif | ||||
|  | ||||
| #ifdef GIO_COMPILATION | ||||
| #define FN_W if (FALSE) g_debug | ||||
| #else | ||||
| #include "gam_error.h" | ||||
| #define FN_W(...) GAM_DEBUG(DEBUG_INFO, __VA_ARGS__) | ||||
| #endif | ||||
|  | ||||
| G_LOCK_EXTERN (fen_lock); | ||||
|  | ||||
| /* Must continue monitoring if: | ||||
|  * 1) I'm subscribed, | ||||
|  * 2) The subscribed children (one of the children has subs) are missing, | ||||
|  * 3) my parent is subscribed (monitoring directory). | ||||
|  */ | ||||
| #define NODE_NEED_MONITOR(f)                                            \ | ||||
|     (NODE_IS_ACTIVE(f) || node_children_num(f) > 0 || NODE_IS_REQUIRED_BY_PARENT(f)) | ||||
|  | ||||
| static int concern_events[] = { | ||||
|     FILE_DELETE, | ||||
|     FILE_RENAME_FROM, | ||||
|     UNMOUNTED, | ||||
|     MOUNTEDOVER, | ||||
| #ifdef GIO_COMPILATION | ||||
|     FILE_MODIFIED, | ||||
|     FILE_ATTRIB, | ||||
| #else | ||||
|     FILE_MODIFIED | FILE_ATTRIB, | ||||
| #endif | ||||
|     FILE_RENAME_TO, | ||||
| }; | ||||
|  | ||||
| node_t *ROOT = NULL; | ||||
|  | ||||
| static void node_emit_one_event(node_t *f, GList *subs, node_t *other, int event); | ||||
| static void node_emit_events(node_t *f, const node_event_t *ne); | ||||
| static int node_event_translate(int event, gboolean pair); | ||||
| static void node_add_event (node_t *f, node_event_t *ev); | ||||
| static node_t* node_new (node_t* parent, const gchar* basename); | ||||
| static void node_delete (node_t* parent); | ||||
| static node_t* node_get_child (node_t *f, const gchar *basename); | ||||
| static void children_add (node_t *p, node_t *f); | ||||
| static void children_remove (node_t *p, node_t *f); | ||||
| static gboolean children_remove_cb (gpointer key, gpointer value, gpointer user_data); | ||||
| static guint node_children_num (node_t *f); | ||||
|  | ||||
| gboolean | ||||
| node_timeval_lt(const GTimeVal *val1, const 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; | ||||
| } | ||||
|  | ||||
| void | ||||
| node_traverse (node_t* node, void(*traverse_cb)(node_t*, gpointer), gpointer user_data) | ||||
| { | ||||
|     GHashTableIter iter; | ||||
|     gpointer value; | ||||
|  | ||||
|     g_assert(traverse_cb); | ||||
|     if (node == NULL) { | ||||
|         node = ROOT; | ||||
|     } | ||||
|  | ||||
|     if (node) { | ||||
|         traverse_cb(node, user_data); | ||||
|     } | ||||
|  | ||||
|     g_hash_table_iter_init (&iter, node->children); | ||||
|     while (g_hash_table_iter_next (&iter, NULL, &value)) { | ||||
|         node_traverse((node_t *)value, traverse_cb, user_data); | ||||
|     } | ||||
| } | ||||
|  | ||||
| node_t* | ||||
| node_find(node_t* node, const gchar* filename, gboolean create_on_missing) | ||||
| { | ||||
|     gchar* str; | ||||
|     gchar* token; | ||||
|     gchar* lasts; | ||||
|     node_t* parent; | ||||
|     node_t* child; | ||||
|      | ||||
|     g_assert (filename && filename[0] == '/'); | ||||
|  | ||||
|     if (node == NULL) { | ||||
|         node = ROOT; | ||||
|     } | ||||
|      | ||||
|     FN_W ("%s %s\n", __func__, filename); | ||||
|  | ||||
|     parent = child = node; | ||||
|     str = g_strdup (filename); | ||||
|      | ||||
|     for (token = strtok_r (str, G_DIR_SEPARATOR_S, &lasts); | ||||
|          token != NULL && child != NULL; | ||||
|          token = strtok_r (NULL, G_DIR_SEPARATOR_S, &lasts)) { | ||||
|         child = node_get_child(parent, token); | ||||
|         if (child) { | ||||
|             parent = child; | ||||
|         } else if (create_on_missing) { | ||||
|             child = node_new (parent, token); | ||||
|             if (child) { | ||||
|                 children_add (parent, child); | ||||
|                 parent = child; | ||||
|                 continue; | ||||
|             } else { | ||||
|                 FN_W ("%s create %s failed", __func__, token); | ||||
|             } | ||||
|         } else { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     g_free (str); | ||||
|     return child; | ||||
| } | ||||
|  | ||||
| gint | ||||
| node_lstat(node_t *f) | ||||
| { | ||||
|     struct stat buf; | ||||
|  | ||||
|     g_assert(!NODE_HAS_STATE(f, NODE_STATE_ASSOCIATED)); | ||||
|  | ||||
|     if (lstat(NODE_NAME(f), &buf) == 0) { | ||||
|         FN_W ("%s %s\n", __func__, NODE_NAME(f)); | ||||
|         FILE_OBJECT(f)->fo_atime = buf.st_atim; | ||||
|         FILE_OBJECT(f)->fo_mtime = buf.st_mtim; | ||||
|         FILE_OBJECT(f)->fo_ctime = buf.st_ctim; | ||||
|         NODE_SET_FLAG(f, NODE_FLAG_STAT_UPDATED | | ||||
|           (S_ISDIR (buf.st_mode) ? NODE_FLAG_DIR : NODE_FLAG_NONE)); | ||||
|         return 0; | ||||
|     } else { | ||||
|         FN_W ("%s(lstat) %s %s\n", __func__, NODE_NAME(f), g_strerror (errno)); | ||||
|     } | ||||
|     return errno; | ||||
| } | ||||
|  | ||||
| void | ||||
| node_create_children_snapshot(node_t *f, gint created_event, gboolean emit) | ||||
| { | ||||
| 	GDir *dir; | ||||
| 	GError *err = NULL; | ||||
|      | ||||
|     FN_W ("%s %s [0x%p]\n", __func__, NODE_NAME(f), f); | ||||
|  | ||||
|     dir = g_dir_open (NODE_NAME(f), 0, &err); | ||||
|     if (dir) { | ||||
|         const char *basename; | ||||
|         node_t *child = NULL; | ||||
|          | ||||
|         while ((basename = g_dir_read_name (dir))) { | ||||
|             node_t* data; | ||||
|             GList *idx; | ||||
|  | ||||
|             child = node_get_child (f, basename); | ||||
|             if (child == NULL) { | ||||
|                 gchar *filename; | ||||
|              | ||||
|                 child = node_new (f, basename); | ||||
|                 children_add (f, child); | ||||
|             } | ||||
|  | ||||
|             if (f->dir_subs) { | ||||
|                 /* We need monitor the new children, or the existed child which | ||||
|                  * is in the DELETED mode. | ||||
|                  */ | ||||
|                 if (!NODE_HAS_STATE(child, NODE_STATE_ASSOCIATED) && | ||||
|                   node_lstat(child) == 0 && port_add(child) == 0) { | ||||
|                     if (emit) { | ||||
|                         /* Emit the whatever event for the new found file. */ | ||||
|                         node_emit_one_event(child, child->dir_subs, NULL, created_event); | ||||
|                         node_emit_one_event(child, child->subs, NULL, created_event); | ||||
|                         node_emit_one_event(child, f->dir_subs, NULL, created_event); | ||||
|                         node_emit_one_event(child, f->subs, NULL, created_event); | ||||
|                     } | ||||
|                 } | ||||
|                 /* else ignore, because it may be deleted. */ | ||||
|             } | ||||
|         } | ||||
|         g_dir_close (dir); | ||||
|  | ||||
|         /* We have finished children snapshot. Any other new added subs should | ||||
|          * directory iterate the snapshot instead of scan directory again. | ||||
|          */ | ||||
|         NODE_SET_FLAG(f, NODE_FLAG_SNAPSHOT_UPDATED); | ||||
|  | ||||
|     } else { | ||||
|         FN_W (err->message); | ||||
|         g_error_free (err); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * If all active children nodes are ported, then cancel monitor the parent | ||||
|  * node. If we know how many children are created, then we can stop accordingly. | ||||
|  * | ||||
|  * Unsafe, need lock.  | ||||
|  */ | ||||
| static void | ||||
| foreach_known_children_scan(gpointer key, gpointer value, gpointer user_data) | ||||
| { | ||||
|     node_t* f = (node_t*)value; | ||||
|      | ||||
|     FN_W ("%s 0x%p %s\n", __func__, f, NODE_NAME(f)); | ||||
|  | ||||
|     if (!NODE_HAS_STATE(f, NODE_STATE_ASSOCIATED)) { | ||||
|         if (node_lstat(f) == 0 && port_add(f) == 0) { | ||||
|             node_emit_one_event(f, f->dir_subs, NULL, FN_EVENT_CREATED); | ||||
|             node_emit_one_event(f, f->subs, NULL, FN_EVENT_CREATED); | ||||
|             if (NODE_PARENT(f)) { | ||||
|                 node_emit_one_event(f, NODE_PARENT(f)->dir_subs, NULL, FN_EVENT_CREATED); | ||||
|                 node_emit_one_event(f, NODE_PARENT(f)->subs, NULL, FN_EVENT_CREATED); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| node_try_delete(node_t* node) | ||||
| { | ||||
|     g_assert (node); | ||||
|  | ||||
|     FN_W ("%s 0x%p %s\n", __func__, node, NODE_NAME(node)); | ||||
|  | ||||
|     /* Try clean children */ | ||||
|     if (node_children_num (node) > 0) { | ||||
|         g_hash_table_foreach_remove(node->children, children_remove_cb, NULL); | ||||
|     } | ||||
|     if (!NODE_NEED_MONITOR(node)) { | ||||
|         /* Clean some flags. */ | ||||
|         /* NODE_CLE_FLAG(node, NODE_FLAG_HAS_SNAPSHOT | NODE_FLAG_STAT_DONE); */ | ||||
|  | ||||
|         /* Now we handle the state. */ | ||||
|         if (NODE_HAS_STATE(node, NODE_STATE_ASSOCIATED)) { | ||||
|             port_remove(node); | ||||
|         } | ||||
|         /* Actually ignore the ROOT node. */ | ||||
|         if (node->state == 0 && NODE_PARENT(node)) { | ||||
|             children_remove(NODE_PARENT(node), node); | ||||
|             /* Do clean instead of returning TRUE. */ | ||||
|             node_delete (node); | ||||
|         } | ||||
|         /* else, we have events, clean event queue? */ | ||||
|     } | ||||
|     return FALSE; | ||||
| } | ||||
|  | ||||
| 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) { | ||||
|             NODE_NAME(f) = g_build_filename(NODE_NAME(parent), basename, NULL); | ||||
|         } else { | ||||
|             NODE_NAME(f) = g_strdup(G_DIR_SEPARATOR_S); | ||||
|         } | ||||
|         f->basename = g_strdup (basename); | ||||
|         /* f->children = g_hash_table_new_full (g_str_hash, g_str_equal, */ | ||||
|         /*   NULL, (GDestroyNotify)node_delete); */ | ||||
|         f->children = g_hash_table_new_full (g_str_hash, g_str_equal, | ||||
|           NULL, NULL); | ||||
| #ifdef GIO_COMPILATION | ||||
|         f->gfile = g_file_new_for_path (NODE_NAME(f)); | ||||
| #endif | ||||
|         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)); | ||||
|     /* Clean flags. */ | ||||
|     f->flag = 0; | ||||
|     g_assert(f->state == 0); | ||||
|     g_assert(!NODE_IS_ACTIVE(f)); | ||||
|     g_assert(g_hash_table_size (f->children) == 0); | ||||
|     g_assert(NODE_PARENT(f) == NULL); | ||||
|     g_hash_table_unref(f->children); | ||||
| #ifdef GIO_COMPILATION | ||||
|     g_object_unref (f->gfile); | ||||
| #endif | ||||
|     g_free(f->basename); | ||||
|     g_free(NODE_NAME(f)); | ||||
|     g_free (f); | ||||
| } | ||||
|  | ||||
| static void | ||||
| children_add (node_t *p, node_t *f) | ||||
| { | ||||
|     FN_W ("%s %s %s\n", __func__, NODE_NAME(p), f->basename); | ||||
|     g_hash_table_insert (p->children, f->basename, f); | ||||
|     NODE_PARENT(f) = p; | ||||
| } | ||||
|  | ||||
| static void | ||||
| children_remove (node_t *p, node_t *f) | ||||
| { | ||||
|     FN_W ("%s %s %s\n", __func__, NODE_NAME(p), f->basename); | ||||
|     g_hash_table_steal (p->children, f->basename); | ||||
|     NODE_PARENT(f) = NULL; | ||||
| } | ||||
|  | ||||
| static node_t * | ||||
| node_get_child (node_t *f, const gchar *basename) | ||||
| { | ||||
|     if (f->children) { | ||||
|         return (node_t *) g_hash_table_lookup (f->children, (gpointer)basename); | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| static guint | ||||
| node_children_num (node_t *f) | ||||
| { | ||||
|     return g_hash_table_size (f->children); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * depth first delete recursively | ||||
|  */ | ||||
| static gboolean | ||||
| children_remove_cb (gpointer key, gpointer value, gpointer user_data) | ||||
| { | ||||
|     return node_try_delete ((node_t*)value); | ||||
| } | ||||
|  | ||||
| gboolean | ||||
| node_class_init() | ||||
| { | ||||
|     ROOT = node_new (NULL, G_DIR_SEPARATOR_S); | ||||
|     if (ROOT == NULL) { | ||||
|         FN_W ("[node] Create ROOT node failed.\n"); | ||||
|         return FALSE; | ||||
|     } | ||||
|  | ||||
|     return port_class_init (node_add_event); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Adjust self on failing to Port | ||||
|  */ | ||||
| void | ||||
| node_adjust_deleted(node_t* f) | ||||
| { | ||||
|     node_t *ancestor; | ||||
|  | ||||
|     FN_W ("%s %s\n", __func__, NODE_NAME(f)); | ||||
|  | ||||
|     for (ancestor = NODE_PARENT(f); | ||||
|          ancestor != NULL; | ||||
|          ancestor = NODE_PARENT(ancestor)) { | ||||
|         /* Stop if we find a node which been already associated or is existed | ||||
|          * and can be associated. | ||||
|          */ | ||||
|         if (NODE_HAS_STATE(ancestor, NODE_STATE_ASSOCIATED) || | ||||
|           (node_lstat(ancestor) == 0 && port_add(ancestor) == 0)) { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* We assume we shouldn't reach here, because Root is always existed and | ||||
|      * associated. But given bugster#6955199, if PORT FS has problems on root, | ||||
|      * we may reach here. So just return ROOT and the whole GIO fen backend will | ||||
|      * fail. | ||||
|      */ | ||||
|     /* g_assert(ancestor != NULL); */ | ||||
| } | ||||
|  | ||||
|  | ||||
| static void | ||||
| node_emit_events(node_t *f, const node_event_t *ne) | ||||
| { | ||||
|     gsize num = sizeof(concern_events)/sizeof(int); | ||||
|     gint i; | ||||
|     int translated_e; | ||||
|     node_t *p; | ||||
|  | ||||
|     if (node_timeval_lt(&f->atv, &ne->ctv)) { | ||||
|         int event = ne->e; | ||||
|  | ||||
|         /* Emit DELETED on the pair_data */ | ||||
|         if (ne->pair_data) { | ||||
|             node_t *from = ne->pair_data; | ||||
|             node_emit_one_event(from, from->dir_subs, NULL, node_event_translate(FILE_DELETE, FALSE)); | ||||
|             node_emit_one_event(from, from->subs, NULL, node_event_translate(FILE_DELETE, FALSE)); | ||||
|         } | ||||
|  | ||||
|         for (i = 0; i < num; i++) { | ||||
|             if (event & concern_events[i]) { | ||||
|                 translated_e = node_event_translate(concern_events[i], FALSE); | ||||
|                 /* Neither GIO or gamin cares about modified events on a | ||||
|                  * directory. | ||||
|                  */ | ||||
| #ifdef GIO_COMPILATION | ||||
|                 if ((concern_events[i] & FILE_MODIFIED) == 0) { | ||||
|                     node_emit_one_event(f, f->dir_subs, NULL, translated_e); | ||||
|                 } | ||||
| #else | ||||
|                 /* Gamin doesn't care about attrib changed events on a directory | ||||
|                  * either. | ||||
|                  */ | ||||
|                 if ((concern_events[i] & (FILE_MODIFIED | FILE_ATTRIB)) == 0) { | ||||
|                     node_emit_one_event(f, f->dir_subs, NULL, translated_e); | ||||
|                 } | ||||
| #endif | ||||
|                 node_emit_one_event(f, f->subs, NULL, translated_e); | ||||
|             } | ||||
|             event &= ~concern_events[i]; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     p = NODE_PARENT(f); | ||||
|     if (p != NULL && node_timeval_lt(&p->atv, &ne->ctv)) { | ||||
|         int event = ne->e; | ||||
|         for (i = 0; i < num; i++) { | ||||
|             if (event & concern_events[i]) { | ||||
|                 translated_e = node_event_translate(concern_events[i], ne->pair_data != NULL); | ||||
|                 node_emit_one_event(f, p->dir_subs, ne->pair_data, translated_e); | ||||
|                 node_emit_one_event(f, p->subs, ne->pair_data, translated_e); | ||||
|             } | ||||
|             event &= ~concern_events[i]; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * node_add_event: | ||||
|  * | ||||
|  */ | ||||
| static void | ||||
| node_add_event (node_t *f, node_event_t *ev) | ||||
| { | ||||
|     FN_W ("%s %d\n", __func__, ev->e); | ||||
|  | ||||
|     /* Clean the events flag early, because all received events need be | ||||
|      * processed in this function. | ||||
|      */ | ||||
|     NODE_CLE_STATE(f, NODE_STATE_HAS_EVENTS); | ||||
|  | ||||
|     /* | ||||
|      * 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. | ||||
|      */ | ||||
|     if (NODE_NEED_MONITOR(f)) { | ||||
|         if (HAS_NO_EXCEPTION_EVENTS(ev->e)) { | ||||
|             if (NODE_HAS_STATE(f, NODE_STATE_ASSOCIATED) || port_add(f) == 0) { | ||||
|                 if ((ev->e & FILE_MODIFIED) && NODE_HAS_FLAG(f, NODE_FLAG_DIR)) { | ||||
|                     if (f->dir_subs) { | ||||
|                         node_create_children_snapshot(f, FN_EVENT_CREATED, TRUE); | ||||
|                     } else { | ||||
|                         g_hash_table_foreach(f->children, foreach_known_children_scan, NULL); | ||||
|                     } | ||||
|                 } | ||||
|             } else { | ||||
|                 /* Emit delete event */ | ||||
|                 ev->e |= FILE_DELETE; | ||||
|  | ||||
|                 node_adjust_deleted(f); | ||||
|             } | ||||
|  | ||||
|         } else { | ||||
|             node_adjust_deleted(f); | ||||
|         } | ||||
|  | ||||
|         /* Send events to clients. */ | ||||
|         node_emit_events (f, ev); | ||||
|          | ||||
|     } else { | ||||
|         /* Send events to clients. */ | ||||
|         node_emit_events (f, ev); | ||||
|  | ||||
|         node_try_delete(f); | ||||
|     } | ||||
|  | ||||
|     if (ev->pair_data) { | ||||
|         node_t *from = ev->pair_data; | ||||
|         g_assert(ev->e == FILE_RENAME_TO); | ||||
|  | ||||
|         if (NODE_NEED_MONITOR(from)) { | ||||
|             /* Clean the events flag, since it may block free this node. */ | ||||
|             NODE_CLE_STATE(from, NODE_STATE_HAS_EVENTS); | ||||
|             node_adjust_deleted(from); | ||||
|         } else { | ||||
|             node_try_delete(from); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     node_event_delete (ev); | ||||
| } | ||||
|  | ||||
| static void | ||||
| node_emit_one_event(node_t *f, GList *subs, node_t *other, int event) | ||||
| { | ||||
|     GList* idx; | ||||
|      | ||||
|     FN_W ("%s %s %d\n", __func__, NODE_NAME(f), event); | ||||
|  | ||||
| #ifdef GIO_COMPILATION | ||||
|     for (idx = subs; idx; idx = idx->next) { | ||||
|         g_file_monitor_emit_event(G_FILE_MONITOR(idx->data), f->gfile, | ||||
|           (other == NULL ? NULL : other->gfile), event); | ||||
|     } | ||||
| #else | ||||
|     for (idx = subs; idx; idx = idx->next) { | ||||
|         gam_server_emit_one_event(NODE_NAME(f), gam_subscription_is_dir(idx->data), event, idx->data, 1); | ||||
|     } | ||||
| #endif | ||||
| } | ||||
|  | ||||
| static int | ||||
| node_event_translate(int event, gboolean pair) | ||||
| { | ||||
| #ifdef GIO_COMPILATION | ||||
|     switch (event) { | ||||
|     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: | ||||
|         return G_FILE_MONITOR_EVENT_CHANGED; | ||||
|     case FILE_RENAME_TO: | ||||
|         if (pair) { | ||||
|             return G_FILE_MONITOR_EVENT_MOVED; | ||||
|         } else { | ||||
|             return G_FILE_MONITOR_EVENT_CREATED; | ||||
|         } | ||||
|     default: | ||||
|         /* case FILE_ACCESS: */ | ||||
|         g_assert_not_reached (); | ||||
|         return -1; | ||||
|     } | ||||
| #else | ||||
|     switch (event) { | ||||
|     case FILE_DELETE: | ||||
|     case FILE_RENAME_FROM: | ||||
|         return GAMIN_EVENT_DELETED; | ||||
|     case MOUNTEDOVER: | ||||
|     case UNMOUNTED: | ||||
|         return GAMIN_EVENT_CHANGED; | ||||
|     case FILE_RENAME_TO: | ||||
|         if (pair) { | ||||
|             return GAMIN_EVENT_MOVED; | ||||
|         } else { | ||||
|             return GAMIN_EVENT_CREATED; | ||||
|         } | ||||
|     default: | ||||
|         if (event & (FILE_ATTRIB | FILE_MODIFIED)) { | ||||
|             return GAMIN_EVENT_CHANGED; | ||||
|         } | ||||
|         /* case FILE_ACCESS: */ | ||||
|         g_assert_not_reached (); | ||||
|         return -1; | ||||
|     } | ||||
| #endif | ||||
| } | ||||
|  | ||||
| node_event_t* | ||||
| node_event_new (int event, gpointer user_data) | ||||
| { | ||||
|     node_event_t *ev; | ||||
|      | ||||
|     if ((ev = g_new (node_event_t, 1)) != NULL) { | ||||
|         g_assert (ev); | ||||
|         ev->e = event; | ||||
|         ev->user_data = user_data; | ||||
|         ev->pair_data = NULL;   /* For renamed file. */ | ||||
|         /* Created timestamp */ | ||||
|         g_get_current_time(&ev->ctv); | ||||
|         ev->rename_tv = ev->ctv; | ||||
|     } | ||||
|     return ev; | ||||
| } | ||||
|  | ||||
| void | ||||
| node_event_delete (node_event_t* ev) | ||||
| { | ||||
|     g_free (ev); | ||||
| } | ||||
| @@ -1,102 +0,0 @@ | ||||
| /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ | ||||
| /* vim:set expandtab ts=4 shiftwidth=4: */ | ||||
| /*  | ||||
|  * Copyright (c) 2008, 2010 Oracle and/or its affiliates, Inc. All rights | ||||
|  * reserved. | ||||
|  * | ||||
|  * 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, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  * Authors: Lin Ma <lin.ma@sun.com> | ||||
|  */ | ||||
|  | ||||
| #include <port.h> | ||||
| #include <gio/gio.h> | ||||
|  | ||||
| #ifndef _FEN_NODE_H_ | ||||
| #define _FEN_NODE_H_ | ||||
|  | ||||
| #ifdef GIO_COMPILATION | ||||
| #define FN_EVENT_CREATED G_FILE_MONITOR_EVENT_CREATED | ||||
| #else | ||||
| #define FN_EVENT_CREATED GAMIN_EVENT_CREATED | ||||
| #endif | ||||
|  | ||||
| #define NODE_STATE_NONE       0x00000000 | ||||
| #define NODE_STATE_ASSOCIATED 0x00000001 /* This is a confilct to NODE_FLAG_STAT_DONE */ | ||||
| #define NODE_STATE_HAS_EVENTS 0x00000002 | ||||
|  | ||||
| #define NODE_FLAG_NONE             0x00000000 | ||||
| #define NODE_FLAG_SNAPSHOT_UPDATED 0x00000001 | ||||
| #define NODE_FLAG_DIR              0x00000002 | ||||
| #define NODE_FLAG_STAT_UPDATED     0x00000004 | ||||
|  | ||||
| #define	NODE_CLE_STATE(f, st)  (f->state &= ~(st)) | ||||
| #define	NODE_SET_STATE(f, st)  (f->state = ((f->state & ~(st)) | (st))) | ||||
| #define	NODE_HAS_STATE(f, st)  (f->state & (st)) | ||||
|  | ||||
| #define	NODE_CLE_FLAG(f, fl)  (f->flag &= ~(fl)) | ||||
| #define	NODE_SET_FLAG(f, fl)  (f->flag = ((f->flag & ~(fl)) | (fl))) | ||||
| #define	NODE_HAS_FLAG(f, fl)  (f->flag & (fl)) | ||||
|  | ||||
| typedef struct node node_t; | ||||
| struct node | ||||
| { | ||||
|     file_obj_t  fobj;           /* Inherit from file_obj_t, must be the first. */ | ||||
|     GSource    *source; | ||||
|     gchar      *basename; | ||||
|     guint32     state; | ||||
| 	guint32     flag; | ||||
|     GTimeVal    atv;            /* Timestamp for the first added sub. */ | ||||
|  | ||||
| 	/* the parent and children of node */ | ||||
|     node_t *parent; | ||||
|     GHashTable *children; /* children in basename */ | ||||
|  | ||||
| 	/* List of subscriptions monitoring this fdata/path */ | ||||
| 	GList *subs; | ||||
| 	GList *dir_subs; | ||||
|  | ||||
| #ifdef GIO_COMPILATION | ||||
|     GFile* gfile; | ||||
| #endif | ||||
| }; | ||||
|  | ||||
| #define FILE_OBJECT(f)                ((file_obj_t *)(f)) | ||||
| #define NODE_NAME(f)                  (FILE_OBJECT(f)->fo_name) | ||||
| #define NODE_PARENT(f)                (((node_t *)f)->parent) | ||||
| #define	NODE_IS_ACTIVE(f)             (f->dir_subs || f->subs) | ||||
| #define	NODE_IS_REQUIRED_BY_PARENT(f) (NODE_PARENT(f) && NODE_PARENT(f)->dir_subs) | ||||
|  | ||||
| gboolean node_timeval_lt(const GTimeVal *val1, const GTimeVal *val2); | ||||
| gboolean node_try_delete(node_t* node); | ||||
| void     node_traverse(node_t* node, void(*traverse_cb)(node_t*, gpointer), gpointer user_data); | ||||
| node_t*  node_find(node_t* node, const gchar* filename, gboolean create_on_missing); | ||||
| gint     node_lstat(node_t *f); | ||||
| void     node_create_children_snapshot(node_t *f, gint created_event, gboolean emit); | ||||
| void     node_adjust_deleted(node_t *f); | ||||
| gboolean node_class_init(); | ||||
|  | ||||
| typedef struct node_event | ||||
| { | ||||
|     int e; | ||||
|     gpointer user_data; | ||||
|     gpointer pair_data; | ||||
|     GTimeVal ctv;               /* Created timestamp */ | ||||
|     GTimeVal rename_tv;         /* Possible rename timestamp */ | ||||
| } node_event_t; | ||||
|  | ||||
| node_event_t* node_event_new (int event, gpointer user_data); | ||||
| void node_event_delete (node_event_t* ev); | ||||
|  | ||||
| #endif /* _FEN_NODE_H_ */ | ||||
| @@ -1,140 +0,0 @@ | ||||
| /* -*- 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, 2010 Oracle and/or its affiliates, Inc. All rights | ||||
|  * reserved. | ||||
|  * | ||||
|  * 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, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  * Authors: Alexander Larsson <alexl@redhat.com> | ||||
|  *          John McCutchan <john@johnmccutchan.com>  | ||||
|  *          Sebastian Dröge <slomo@circular-chaos.org> | ||||
|  *          Lin Ma <lin.ma@sun.com> | ||||
|  */ | ||||
|  | ||||
| #include "config.h" | ||||
|  | ||||
| #include "gfendirectorymonitor.h" | ||||
| #include <gio/giomodule.h> | ||||
|  | ||||
| #include "fen-helper.h" | ||||
|  | ||||
| struct _GFenDirectoryMonitor | ||||
| { | ||||
|     GLocalDirectoryMonitor parent_instance; | ||||
|     gboolean enabled; | ||||
| }; | ||||
|  | ||||
| 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->enabled) { | ||||
|         fen_remove (G_LOCAL_DIRECTORY_MONITOR (self)->dirname, self, TRUE); | ||||
|         self->enabled = FALSE; | ||||
|     } | ||||
|  | ||||
|     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 */ | ||||
|     fen_add (dirname, self, TRUE); | ||||
|     self->enabled = 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->enabled) { | ||||
|         fen_remove (G_LOCAL_DIRECTORY_MONITOR (self)->dirname, self, TRUE); | ||||
|         self->enabled = FALSE; | ||||
|     } | ||||
|      | ||||
|     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; | ||||
| } | ||||
| @@ -1,56 +0,0 @@ | ||||
| /* -*- 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, 2010 Oracle and/or its affiliates, Inc. All rights | ||||
|  * reserved. | ||||
|  * | ||||
|  * 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, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  * Authors: Alexander Larsson <alexl@redhat.com> | ||||
|  *          John McCutchan <john@johnmccutchan.com>  | ||||
|  *          Sebastian Dröge <slomo@circular-chaos.org> | ||||
|  *          Lin Ma <lin.ma@sun.com> | ||||
|  */ | ||||
|  | ||||
| #ifndef __G_FEN_DIRECTORY_MONITOR_H__ | ||||
| #define __G_FEN_DIRECTORY_MONITOR_H__ | ||||
|  | ||||
| #include <glib-object.h> | ||||
| #include <string.h> | ||||
| #include <gio/glocaldirectorymonitor.h> | ||||
| #include <gio/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__ */ | ||||
| @@ -1,140 +0,0 @@ | ||||
| /* -*- 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, 2010 Oracle and/or its affiliates, Inc. All rights | ||||
|  * reserved. | ||||
|  * | ||||
|  * 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, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  * Authors: Alexander Larsson <alexl@redhat.com> | ||||
|  *          John McCutchan <john@johnmccutchan.com>  | ||||
|  *          Sebastian Dröge <slomo@circular-chaos.org> | ||||
|  *          Lin Ma <lin.ma@sun.com> | ||||
|  */ | ||||
|  | ||||
| #include "config.h" | ||||
|  | ||||
| #include "gfenfilemonitor.h" | ||||
| #include <gio/giomodule.h> | ||||
|  | ||||
| #include "fen-helper.h" | ||||
|  | ||||
| struct _GFenFileMonitor | ||||
| { | ||||
|     GLocalFileMonitor parent_instance; | ||||
|     gboolean enabled; | ||||
| }; | ||||
|  | ||||
| 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->enabled) { | ||||
|         fen_remove (G_LOCAL_FILE_MONITOR (self)->filename, self, FALSE); | ||||
|         self->enabled = FALSE; | ||||
|     } | ||||
|      | ||||
|     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 */ | ||||
|     fen_add (filename, self, FALSE); | ||||
|     self->enabled = TRUE; | ||||
|  | ||||
|     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->enabled) { | ||||
|         fen_remove (G_LOCAL_FILE_MONITOR (self)->filename, self, FALSE); | ||||
|         self->enabled = FALSE; | ||||
|     } | ||||
|      | ||||
|     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; | ||||
| } | ||||
| @@ -1,57 +0,0 @@ | ||||
| /* -*- 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, 2010 Oracle and/or its affiliates, Inc. All rights | ||||
|  * reserved. | ||||
|  * | ||||
|  * 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, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  * Authors: Alexander Larsson <alexl@redhat.com> | ||||
|  *          John McCutchan <john@johnmccutchan.com>  | ||||
|  *          Sebastian Dröge <slomo@circular-chaos.org> | ||||
|  *          Lin Ma <lin.ma@sun.com> | ||||
|  */ | ||||
|  | ||||
| #ifndef __G_FEN_FILE_MONITOR_H__ | ||||
| #define __G_FEN_FILE_MONITOR_H__ | ||||
|  | ||||
| #include <glib-object.h> | ||||
| #include <string.h> | ||||
| #include <gio/gfilemonitor.h> | ||||
| #include <gio/glocalfilemonitor.h> | ||||
| #include <gio/giomodule.h> | ||||
|  | ||||
| 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__ */ | ||||
		Reference in New Issue
	
	Block a user