From 7f0e5a0b7f37e79869dd07588e8e5bd5c6d293ae Mon Sep 17 00:00:00 2001 From: Mart Raudsepp Date: Mon, 18 Sep 2006 05:10:03 +0000 Subject: Updates from Fedora that should bring the package close to the state of upstream right now. This solves gam_server waking up tens of times every second even if inotify is used, mostly thanks to the new inotify backend from upstream being more sane and closer to gnome-vfs code. Probably has more affect on kdelibs-3.5 than on GNOME-2.16 (gnome-vfs has a sane inotify backend already there, KDE seems to use FAM API at least here). I get zero polling now usually, instead of total context switch spammage as earlier. In general it shouldn't now wake up more than once every 4 seconds. --- app-admin/gamin/Manifest | 45 + app-admin/gamin/files/digest-gamin-0.1.7 | 6 + app-admin/gamin/files/gamin-0.1.7-sigaction.patch | 60 + app-admin/gamin/files/gamin-dont-close-fd.patch | 72 + .../gamin/files/gamin-flush-buffer-on-reset.patch | 15 + app-admin/gamin/files/gamin-inotify-fix.patch | 93 + .../gamin/files/gamin-new-inotify-backend.patch | 3643 ++++++++++++++++++++ app-admin/gamin/files/gamin-no-timers.patch | 284 ++ app-admin/gamin/files/gamin-stdin-devnull.patch | 36 + app-admin/gamin/files/gamin-timer-on-demand.patch | 73 + app-admin/gamin/gamin-0.1.7.ebuild | 73 + 11 files changed, 4400 insertions(+) create mode 100644 app-admin/gamin/Manifest create mode 100644 app-admin/gamin/files/digest-gamin-0.1.7 create mode 100644 app-admin/gamin/files/gamin-0.1.7-sigaction.patch create mode 100644 app-admin/gamin/files/gamin-dont-close-fd.patch create mode 100644 app-admin/gamin/files/gamin-flush-buffer-on-reset.patch create mode 100644 app-admin/gamin/files/gamin-inotify-fix.patch create mode 100644 app-admin/gamin/files/gamin-new-inotify-backend.patch create mode 100644 app-admin/gamin/files/gamin-no-timers.patch create mode 100644 app-admin/gamin/files/gamin-stdin-devnull.patch create mode 100644 app-admin/gamin/files/gamin-timer-on-demand.patch create mode 100644 app-admin/gamin/gamin-0.1.7.ebuild diff --git a/app-admin/gamin/Manifest b/app-admin/gamin/Manifest new file mode 100644 index 0000000..665ce79 --- /dev/null +++ b/app-admin/gamin/Manifest @@ -0,0 +1,45 @@ +AUX gamin-0.1.7-sigaction.patch 2094 RMD160 4adb9ebdff2ff9fe4c0d187820257aeab39cb5f0 SHA1 7c2cdb5c946bfe102388a8e0c4cfb820afdb2fa0 SHA256 37b34efa0c7fe80cdda92b638f5663838d98547f511c77f07f86d58b5426a3a2 +MD5 c6bee72840f1ca2c4fb10fd4d0cba679 files/gamin-0.1.7-sigaction.patch 2094 +RMD160 4adb9ebdff2ff9fe4c0d187820257aeab39cb5f0 files/gamin-0.1.7-sigaction.patch 2094 +SHA256 37b34efa0c7fe80cdda92b638f5663838d98547f511c77f07f86d58b5426a3a2 files/gamin-0.1.7-sigaction.patch 2094 +AUX gamin-dont-close-fd.patch 1855 RMD160 f66ccd63aa54631c3dd8ce5bfbd7850527cfb424 SHA1 cb668a95dab4946f12e6fcfb3d9123419bd10a81 SHA256 4bca072677e94b7772ee84cfea5777fc3593ee018ca58c8f7e0db65024ad4fb8 +MD5 3e6228d3bcdf46459ae37963b7434287 files/gamin-dont-close-fd.patch 1855 +RMD160 f66ccd63aa54631c3dd8ce5bfbd7850527cfb424 files/gamin-dont-close-fd.patch 1855 +SHA256 4bca072677e94b7772ee84cfea5777fc3593ee018ca58c8f7e0db65024ad4fb8 files/gamin-dont-close-fd.patch 1855 +AUX gamin-flush-buffer-on-reset.patch 497 RMD160 452596c00ef970d278febf18a4b5289a65331993 SHA1 a847fb650609fa2ac671ed6c1b79db6ab9b5de9e SHA256 ee367da8774e43cfee29ea1ed3673fcb3ebf1e61ee6d3511ff5d5b5bb9676be1 +MD5 7c1341735c4ee16239713606f55c51d9 files/gamin-flush-buffer-on-reset.patch 497 +RMD160 452596c00ef970d278febf18a4b5289a65331993 files/gamin-flush-buffer-on-reset.patch 497 +SHA256 ee367da8774e43cfee29ea1ed3673fcb3ebf1e61ee6d3511ff5d5b5bb9676be1 files/gamin-flush-buffer-on-reset.patch 497 +AUX gamin-inotify-fix.patch 2900 RMD160 c6a4d843880bfd8781e98184bea8234edb401486 SHA1 b9e90827730740c62d6b636e6ceb97e1588146dc SHA256 5c6fafd88cd486392492d1f088460aeeb83e6570f0d746fc7d3dfc8e678091d3 +MD5 56cdf4d179fef49c08f0c8655a1dc441 files/gamin-inotify-fix.patch 2900 +RMD160 c6a4d843880bfd8781e98184bea8234edb401486 files/gamin-inotify-fix.patch 2900 +SHA256 5c6fafd88cd486392492d1f088460aeeb83e6570f0d746fc7d3dfc8e678091d3 files/gamin-inotify-fix.patch 2900 +AUX gamin-new-inotify-backend.patch 99430 RMD160 1df3158c04db0ed144c001cbcdb364a9b65ede85 SHA1 a7b6775699514a951a60d54beb102f56a80cbc1c SHA256 e13763617867afd9873c78b2d073e8ef6f4388837a0461cd9c600217a14897cf +MD5 413015c52f80b43df66a7b2b5d2e8489 files/gamin-new-inotify-backend.patch 99430 +RMD160 1df3158c04db0ed144c001cbcdb364a9b65ede85 files/gamin-new-inotify-backend.patch 99430 +SHA256 e13763617867afd9873c78b2d073e8ef6f4388837a0461cd9c600217a14897cf files/gamin-new-inotify-backend.patch 99430 +AUX gamin-no-timers.patch 7904 RMD160 686a7a517da9e6951d8aeeb3c3b0eb8d71bd89cc SHA1 6d0b1aca14f339ac40b8a4ada439c7884b489eaf SHA256 6b3fd0a9e380238b02e0b9e811704385a98bf09ddcc550ab77e991a2b4166372 +MD5 4543065e8bd2ca72ca2e42177178621a files/gamin-no-timers.patch 7904 +RMD160 686a7a517da9e6951d8aeeb3c3b0eb8d71bd89cc files/gamin-no-timers.patch 7904 +SHA256 6b3fd0a9e380238b02e0b9e811704385a98bf09ddcc550ab77e991a2b4166372 files/gamin-no-timers.patch 7904 +AUX gamin-stdin-devnull.patch 941 RMD160 2387e8b830b03fca10f4aad4b0b94ab1b0386554 SHA1 516cf3bf0144dd2aab7294f2c102c7badb6b5a6e SHA256 d11e074d3f6f60bbc9c50fdfd26d10452efc6977069e1e7053b34c15c37f9749 +MD5 2916fe4b8475ff590dde1e3af2c31871 files/gamin-stdin-devnull.patch 941 +RMD160 2387e8b830b03fca10f4aad4b0b94ab1b0386554 files/gamin-stdin-devnull.patch 941 +SHA256 d11e074d3f6f60bbc9c50fdfd26d10452efc6977069e1e7053b34c15c37f9749 files/gamin-stdin-devnull.patch 941 +AUX gamin-timer-on-demand.patch 2158 RMD160 458e15efb47c34145a20a4c0ced4e317495184ac SHA1 4dd5d64bf4bfc2e12709a7fbd72ffa3db1524e06 SHA256 581406841da596fda8125d96ca6f118991f0e1242ff334e61f91f0eaf4f6d3c0 +MD5 1e32b6a24c047c3333c6a48cd6c5faf5 files/gamin-timer-on-demand.patch 2158 +RMD160 458e15efb47c34145a20a4c0ced4e317495184ac files/gamin-timer-on-demand.patch 2158 +SHA256 581406841da596fda8125d96ca6f118991f0e1242ff334e61f91f0eaf4f6d3c0 files/gamin-timer-on-demand.patch 2158 +DIST gamin-0.1.7-freebsd.patch.bz2 5217 RMD160 22ee7ccac4cca87188bf299404186d3c5ea08475 SHA1 31ca1fdf8e21f9a0ac3a98b7865e3ab0d3c6c471 SHA256 8c77200b1fac6f1a65819a38ab54767f8a25ffbe990cde5f500eec040d815fbf +DIST gamin-0.1.7.tar.gz 542061 RMD160 440b6afc682806b33aa275e3b8a593dc7e3f93de SHA1 bde971a10a3510626d501db3a746370c8931612a SHA256 8272c64356e1310353f2411b2cabd211468edbf8ce312d58aeef55889e79361a +EBUILD gamin-0.1.7.ebuild 2146 RMD160 b4102373b6cbb17b0ef61ea310c4daf9a118c4c1 SHA1 dbc1b31da24fd6ed35e0584bae8d7b9086efc762 SHA256 962656a3f618fdeccd796868128d83777c77b046b5e970b7e8fbd015c2751a6e +MD5 3a4be71ee1f2bccb8e17bdafb1f6d7f7 gamin-0.1.7.ebuild 2146 +RMD160 b4102373b6cbb17b0ef61ea310c4daf9a118c4c1 gamin-0.1.7.ebuild 2146 +SHA256 962656a3f618fdeccd796868128d83777c77b046b5e970b7e8fbd015c2751a6e gamin-0.1.7.ebuild 2146 +MISC ChangeLog 618 RMD160 2997214c80b9bf3b8f89e1de953b007c8354b845 SHA1 25e513c5e550e474dece2fa2f4f6688bf8711adc SHA256 d2499af2b08e58ae58f2895d6a86d519b377de29502192162ee7498ad2c728ac +MD5 f5bd40e6ef1b092c3077e84a3547f378 ChangeLog 618 +RMD160 2997214c80b9bf3b8f89e1de953b007c8354b845 ChangeLog 618 +SHA256 d2499af2b08e58ae58f2895d6a86d519b377de29502192162ee7498ad2c728ac ChangeLog 618 +MD5 469372237b50836ef50ad51b93c9bf96 files/digest-gamin-0.1.7 497 +RMD160 7062a11132fc5e1d4f3313ce32e42873ebf6d4f5 files/digest-gamin-0.1.7 497 +SHA256 65c5ef16d64495211c6d24b345c15af401df9ad0c4baad29eb3b2095421f6ef9 files/digest-gamin-0.1.7 497 diff --git a/app-admin/gamin/files/digest-gamin-0.1.7 b/app-admin/gamin/files/digest-gamin-0.1.7 new file mode 100644 index 0000000..8a5560f --- /dev/null +++ b/app-admin/gamin/files/digest-gamin-0.1.7 @@ -0,0 +1,6 @@ +MD5 fe942c8202fdee10e1bf139ae8a698a0 gamin-0.1.7-freebsd.patch.bz2 5217 +RMD160 22ee7ccac4cca87188bf299404186d3c5ea08475 gamin-0.1.7-freebsd.patch.bz2 5217 +SHA256 8c77200b1fac6f1a65819a38ab54767f8a25ffbe990cde5f500eec040d815fbf gamin-0.1.7-freebsd.patch.bz2 5217 +MD5 1a1991ffa749b7dc2cc0573d6a2867b8 gamin-0.1.7.tar.gz 542061 +RMD160 440b6afc682806b33aa275e3b8a593dc7e3f93de gamin-0.1.7.tar.gz 542061 +SHA256 8272c64356e1310353f2411b2cabd211468edbf8ce312d58aeef55889e79361a gamin-0.1.7.tar.gz 542061 diff --git a/app-admin/gamin/files/gamin-0.1.7-sigaction.patch b/app-admin/gamin/files/gamin-0.1.7-sigaction.patch new file mode 100644 index 0000000..3457c3a --- /dev/null +++ b/app-admin/gamin/files/gamin-0.1.7-sigaction.patch @@ -0,0 +1,60 @@ +diff -urN gamin-0.1.7/lib/gam_error.c /gnome/head/cvs/gamin/lib/gam_error.c +--- gamin-0.1.7/lib/gam_error.c 2005-09-08 09:46:45.000000000 +0200 ++++ /gnome/head/cvs/gamin/lib/gam_error.c 2006-06-01 11:59:14.000000000 +0200 +@@ -76,7 +76,7 @@ + gam_error_init(void) + { + if (initialized == 0) { +- signal_handler prev; ++ struct sigaction oldact; + + initialized = 1; + +@@ -89,11 +89,12 @@ + gam_error_handle_signal(); + } + +- prev = signal(SIGUSR2, gam_error_signal); +- /* if there is already an handler switch back to the original +- * to avoid disturbing the application behaviour */ +- if ((prev != SIG_IGN) && (prev != SIG_DFL) && (prev != NULL)) +- signal(SIGUSR2, prev); ++ /* if there is already an handler, leave it as is to ++ * avoid disturbing the application's behaviour */ ++ if (sigaction (SIGUSR2, NULL, &oldact) == 0) { ++ if (oldact.sa_handler == NULL && oldact.sa_sigaction == NULL) ++ signal(SIGUSR2, gam_error_signal); ++ } + } + } + +diff -urN gamin-0.1.7/libgamin/gam_error.c /gnome/head/cvs/gamin/libgamin/gam_error.c +--- gamin-0.1.7/libgamin/gam_error.c 2005-10-25 16:16:51.000000000 +0200 ++++ /gnome/head/cvs/gamin/libgamin/gam_error.c 2006-06-01 12:00:09.000000000 +0200 +@@ -76,7 +76,7 @@ + gam_error_init(void) + { + if (initialized == 0) { +- signal_handler prev; ++ struct sigaction oldact; + + initialized = 1; + +@@ -89,11 +89,12 @@ + gam_error_handle_signal(); + } + +- prev = signal(SIGUSR2, gam_error_signal); +- /* if there is already an handler switch back to the original +- * to avoid disturbing the application behaviour */ +- if ((prev != SIG_IGN) && (prev != SIG_DFL) && (prev != NULL)) +- signal(SIGUSR2, prev); ++ /* if there is already an handler, leave it as is to ++ * avoid disturbing the application's behaviour */ ++ if (sigaction (SIGUSR2, NULL, &oldact) == 0) { ++ if (oldact.sa_handler == NULL && oldact.sa_sigaction == NULL) ++ signal(SIGUSR2, gam_error_signal); ++ } + } + } + diff --git a/app-admin/gamin/files/gamin-dont-close-fd.patch b/app-admin/gamin/files/gamin-dont-close-fd.patch new file mode 100644 index 0000000..d95f599 --- /dev/null +++ b/app-admin/gamin/files/gamin-dont-close-fd.patch @@ -0,0 +1,72 @@ +Index: libgamin/gam_api.c +=================================================================== +RCS file: /cvs/gnome/gamin/libgamin/gam_api.c,v +retrieving revision 1.39 +diff -u -p -r1.39 gam_api.c +--- libgamin/gam_api.c 5 Aug 2005 14:06:49 -0000 1.39 ++++ libgamin/gam_api.c 16 Aug 2006 14:59:27 -0000 +@@ -744,7 +744,6 @@ retry: + return(0); + + failed: +- close(fd); + return (-1); + } + +@@ -891,39 +890,38 @@ gamin_try_reconnect(GAMDataPtr conn, int + /* + * try to reopen a connection to the server + */ +- close(fd); + newfd = gamin_connect_unix_socket(socket_name); +- + free(socket_name); + if (newfd < 0) { + return (-1); + } + +- if (newfd != fd) { +- /* +- * reuse the same descriptor +- */ +- ret = dup2(newfd, fd); +- if (ret < 0) { +- gam_error(DEBUG_INFO, +- "Failed to reuse descriptor %d on reconnect\n", +- fd); +- close(newfd); +- return (-1); +- } +- } +- + /* + * seems we managed to rebuild a connection to the server. + * start the authentication again and resubscribe all existing + * monitoring commands. + */ +- ret = gamin_write_credential_byte(fd); ++ ret = gamin_write_credential_byte(newfd); + if (ret != 0) { +- close(fd); ++ close(newfd); + return (-1); + } + ++ /* ++ * reuse the same descriptor. We never close the original fd, dup2 ++ * atomically overwrites it and closes the original. This way we ++ * never leave the original fd closed, since that can cause trouble ++ * if the app keeps the fd around. ++ */ ++ ret = dup2(newfd, fd); ++ close(newfd); ++ if (ret < 0) { ++ gam_error(DEBUG_INFO, ++ "Failed to reuse descriptor %d on reconnect\n", ++ fd); ++ return (-1); ++ } ++ + nb_req = gamin_data_reset(conn, &reqs); + if (reqs != NULL) { + for (i = 0; i < nb_req;i++) { diff --git a/app-admin/gamin/files/gamin-flush-buffer-on-reset.patch b/app-admin/gamin/files/gamin-flush-buffer-on-reset.patch new file mode 100644 index 0000000..e9f3253 --- /dev/null +++ b/app-admin/gamin/files/gamin-flush-buffer-on-reset.patch @@ -0,0 +1,15 @@ +Index: libgamin/gam_data.c +=================================================================== +RCS file: /cvs/gnome/gamin/libgamin/gam_data.c,v +retrieving revision 1.18 +diff -u -p -r1.18 gam_data.c +--- libgamin/gam_data.c 11 Aug 2006 09:29:53 -0000 1.18 ++++ libgamin/gam_data.c 28 Aug 2006 09:25:26 -0000 +@@ -539,6 +539,7 @@ gamin_data_reset(GAMDataPtr conn, GAMReq + conn->reqno = 1; + conn->restarted = 1; + conn->evn_ready = 0; ++ conn->evn_read = 0; + return(conn->req_nr); + } + diff --git a/app-admin/gamin/files/gamin-inotify-fix.patch b/app-admin/gamin/files/gamin-inotify-fix.patch new file mode 100644 index 0000000..26a3cce --- /dev/null +++ b/app-admin/gamin/files/gamin-inotify-fix.patch @@ -0,0 +1,93 @@ +Index: server/gam_connection.c +=================================================================== +RCS file: /cvs/gnome/gamin/server/gam_connection.c,v +retrieving revision 1.31 +diff -u -p -r1.31 gam_connection.c +--- server/gam_connection.c 5 Sep 2006 14:26:56 -0000 1.31 ++++ server/gam_connection.c 8 Sep 2006 12:42:41 -0000 +@@ -108,7 +108,8 @@ gam_connection_close(GamConnDataPtr conn + g_assert(conn->source); + + /* Kill the queue event source */ +- g_source_remove (conn->eq_source); ++ if (conn->eq_source != 0) ++ g_source_remove (conn->eq_source); + /* Flush the event queue */ + gam_eq_flush (conn->eq, conn); + /* Kill the event queue */ +Index: server/gam_inotify.c +=================================================================== +RCS file: /cvs/gnome/gamin/server/gam_inotify.c,v +retrieving revision 1.52 +diff -u -p -r1.52 gam_inotify.c +--- server/gam_inotify.c 5 Sep 2006 14:17:20 -0000 1.52 ++++ server/gam_inotify.c 8 Sep 2006 12:42:41 -0000 +@@ -161,6 +161,9 @@ gboolean + gam_inotify_add_subscription (GamSubscription *sub) + { + ih_sub_t *isub = NULL; ++ ++ gam_listener_add_subscription(gam_subscription_get_listener(sub), sub); ++ + isub = ih_sub_new (gam_subscription_get_path (sub), gam_subscription_is_dir (sub), 0, sub); + + if (!ih_sub_add (isub)) +Index: server/inotify-helper.c +=================================================================== +RCS file: /cvs/gnome/gamin/server/inotify-helper.c,v +retrieving revision 1.1 +diff -u -p -r1.1 inotify-helper.c +--- server/inotify-helper.c 5 Sep 2006 00:49:00 -0000 1.1 ++++ server/inotify-helper.c 8 Sep 2006 12:42:41 -0000 +@@ -161,28 +161,21 @@ static void + ih_sub_foreach_worker (void *callerdata, gboolean (*f)(ih_sub_t *sub, void *callerdata), gboolean free) + { + GList *l = NULL; +- GList *removed = NULL; ++ GList *next = NULL; + + G_LOCK(inotify_lock); + +- for (l = sub_list; l; l = l->next) ++ for (l = sub_list; l; l = next) + { + ih_sub_t *sub = l->data; +- ++ next = l->next; ++ + if (f(sub, callerdata)) + { +- removed = g_list_prepend (removed, l); +- ih_sub_cancel (sub); ++ ih_sub_cancel (sub); /* Removes sub from sub_list */ + if (free) + ih_sub_free (sub); + } +- } +- +- for (l = removed; l ; l = l->next) +- { +- GList *llink = l->data; +- sub_list = g_list_remove_link (sub_list, llink); +- g_list_free_1 (llink); + } + + G_UNLOCK(inotify_lock); +Index: server/inotify-sub.c +=================================================================== +RCS file: /cvs/gnome/gamin/server/inotify-sub.c,v +retrieving revision 1.1 +diff -u -p -r1.1 inotify-sub.c +--- server/inotify-sub.c 5 Sep 2006 00:49:00 -0000 1.1 ++++ server/inotify-sub.c 8 Sep 2006 12:42:41 -0000 +@@ -71,9 +71,7 @@ gchar *ih_sub_get_dirname (gchar *pathna + static + gchar *ih_sub_get_filename (gchar *pathname) + { +- gchar *out; +- // FIXME: return filename here +- return out; ++ return g_path_get_basename (pathname); + } + + static diff --git a/app-admin/gamin/files/gamin-new-inotify-backend.patch b/app-admin/gamin/files/gamin-new-inotify-backend.patch new file mode 100644 index 0000000..5917559 --- /dev/null +++ b/app-admin/gamin/files/gamin-new-inotify-backend.patch @@ -0,0 +1,3643 @@ +--- /dev/null 2006-08-28 15:22:40.902752500 +0200 ++++ gamin-0.1.7/server/inotify-missing.h 2006-09-05 11:01:21.000000000 +0200 +@@ -0,0 +1,34 @@ ++/* inotify-helper.h - GNOME VFS Monitor using inotify ++ ++ Copyright (C) 2006 John McCutchan ++ ++ The Gnome Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Library General Public License as ++ published by the Free Software Foundation; either version 2 of the ++ License, or (at your option) any later version. ++ ++ The Gnome 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 ++ Library General Public License for more details. ++ ++ You should have received a copy of the GNU Library General Public ++ License along with the Gnome Library; see the file COPYING.LIB. If not, ++ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, ++ Boston, MA 02111-1307, USA. ++ ++ Author: John McCutchan ++*/ ++ ++ ++#ifndef __INOTIFY_MISSING_H ++#define __INOTIFY_MISSING_H ++ ++#include "inotify-sub.h" ++ ++void im_startup (void (*missing_cb)(ih_sub_t *sub)); ++void im_add (ih_sub_t *sub); ++void im_rm (ih_sub_t *sub); ++void im_diag_dump (GIOChannel *ioc); ++ ++#endif /* __INOTIFY_MISSING_H */ +--- /dev/null 2006-08-28 15:22:40.902752500 +0200 ++++ gamin-0.1.7/server/inotify-kernel.h 2006-09-05 11:01:21.000000000 +0200 +@@ -0,0 +1,40 @@ ++/* ++ Copyright (C) 2006 John McCutchan ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; version 2. ++ ++ This program 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 General Public License version 2 for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software Foundation, ++ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++*/ ++ ++#ifndef __INOTIFY_KERNEL_H ++#define __INOTIFY_KERNEL_H ++ ++typedef struct ik_event_s { ++ gint32 wd; ++ guint32 mask; ++ guint32 cookie; ++ guint32 len; ++ char * name; ++ struct ik_event_s *pair; ++} ik_event_t; ++ ++gboolean ik_startup (void (*cb)(ik_event_t *event)); ++ik_event_t *ik_event_new_dummy (const char *name, gint32 wd, guint32 mask); ++void ik_event_free (ik_event_t *event); ++ ++gint32 ik_watch(const char *path, guint32 mask, int *err); ++int ik_ignore(const char *path, gint32 wd); ++ ++/* The miss count will probably be enflated */ ++void ik_move_stats (guint32 *matches, guint32 *misses); ++const char *ik_mask_to_string (guint32 mask); ++ ++#endif +--- /dev/null 2006-08-28 15:22:40.902752500 +0200 ++++ gamin-0.1.7/server/inotify-diag.h 2006-09-05 11:01:21.000000000 +0200 +@@ -0,0 +1,30 @@ ++/* inotify-helper.h - GNOME VFS Monitor using inotify ++ ++ Copyright (C) 2006 John McCutchan ++ ++ The Gnome Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Library General Public License as ++ published by the Free Software Foundation; either version 2 of the ++ License, or (at your option) any later version. ++ ++ The Gnome 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 ++ Library General Public License for more details. ++ ++ You should have received a copy of the GNU Library General Public ++ License along with the Gnome Library; see the file COPYING.LIB. If not, ++ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, ++ Boston, MA 02111-1307, USA. ++ ++ Author: John McCutchan ++*/ ++ ++ ++#ifndef __INOTIFY_DIAG_H ++#define __INOTIFY_DIAG_H ++ ++void id_startup (void); ++gboolean id_dump (gpointer userdata); ++ ++#endif /* __INOTIFY_DIAG_H */ +--- gamin-0.1.7/server/Makefile.am.new-inotify-backend 2005-08-26 13:52:19.000000000 +0200 ++++ gamin-0.1.7/server/Makefile.am 2006-09-05 11:01:21.000000000 +0200 +@@ -51,6 +51,12 @@ + + if ENABLE_INOTIFY + gam_server_SOURCES += gam_inotify.c gam_inotify.h \ ++ inotify-helper.c inotify-helper.h \ ++ inotify-kernel.c inotify-kernel.h \ ++ inotify-missing.c inotify-missing.h \ ++ inotify-path.c inotify-path.h \ ++ inotify-sub.c inotify-sub.h \ ++ inotify-diag.c inotify-diag.h \ + local_inotify.h local_inotify_syscalls.h + endif + +--- gamin-0.1.7/server/local_inotify_syscalls.h.new-inotify-backend 2005-08-17 15:50:04.000000000 +0200 ++++ gamin-0.1.7/server/local_inotify_syscalls.h 2006-09-05 11:01:21.000000000 +0200 +@@ -1,7 +1,9 @@ + #ifndef _LINUX_INOTIFY_SYSCALLS_H + #define _LINUX_INOTIFY_SYSCALLS_H + ++#include + #include ++#include + + #if defined(__i386__) + # define __NR_inotify_init 291 +--- /dev/null 2006-08-28 15:22:40.902752500 +0200 ++++ gamin-0.1.7/server/inotify-kernel.c 2006-09-05 11:01:21.000000000 +0200 +@@ -0,0 +1,685 @@ ++/* ++ Copyright (C) 2006 John McCutchan ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; version 2. ++ ++ This program 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 General Public License version 2 for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software Foundation, ++ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++*/ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "inotify-kernel.h" ++ ++/* Just include the local headers to stop all the pain */ ++#include "local_inotify.h" ++#include "local_inotify_syscalls.h" ++#if 0 ++#ifdef HAVE_SYS_INOTIFY_H ++/* We don't actually include the libc header, because there has been ++ * problems with libc versions that was built without inotify support. ++ * Instead we use the local version. ++ */ ++#include "local_inotify.h" ++#include "local_inotify_syscalls.h" ++#elif defined (HAVE_LINUX_INOTIFY_H) ++#include ++#include "local_inotify_syscalls.h" ++#endif ++#endif ++ ++/* Timings for pairing MOVED_TO / MOVED_FROM events */ ++#define PROCESS_EVENTS_TIME 1000 /* milliseconds (1 hz) */ ++#define DEFAULT_HOLD_UNTIL_TIME 0 /* 0 millisecond */ ++#define MOVE_HOLD_UNTIL_TIME 0 /* 0 milliseconds */ ++ ++static int inotify_instance_fd = -1; ++static GQueue *events_to_process = NULL; ++static GQueue *event_queue = NULL; ++static GHashTable * cookie_hash = NULL; ++static GIOChannel *inotify_read_ioc; ++static GPollFD ik_poll_fd; ++static gboolean ik_poll_fd_enabled = TRUE; ++static void (*user_cb)(ik_event_t *event); ++ ++static gboolean ik_read_callback (gpointer user_data); ++static gboolean ik_process_eq_callback (gpointer user_data); ++ ++static guint32 ik_move_matches = 0; ++static guint32 ik_move_misses = 0; ++ ++static gboolean process_eq_running = FALSE; ++ ++/* We use the lock from inotify-helper.c ++ * ++ * There are two places that we take this lock ++ * ++ * 1) In ik_read_callback ++ * ++ * 2) ik_process_eq_callback. ++ * ++ * ++ * The rest of locking is taken care of in inotify-helper.c ++ */ ++G_LOCK_EXTERN (inotify_lock); ++ ++typedef struct ik_event_internal { ++ ik_event_t *event; ++ gboolean seen; ++ gboolean sent; ++ GTimeVal hold_until; ++ struct ik_event_internal *pair; ++} ik_event_internal_t; ++ ++/* In order to perform non-sleeping inotify event chunking we need ++ * a custom GSource ++ */ ++static gboolean ++ik_source_prepare (GSource *source, ++ gint *timeout) ++{ ++ return FALSE; ++} ++ ++static gboolean ++ik_source_timeout (gpointer data) ++{ ++ GSource *source = (GSource *)data; ++ ++ /* Re-active the PollFD */ ++ g_source_add_poll (source, &ik_poll_fd); ++ g_source_unref (source); ++ ik_poll_fd_enabled = TRUE; ++ ++ return FALSE; ++} ++ ++#define MAX_PENDING_COUNT 2 ++#define PENDING_THRESHOLD(qsize) ((qsize) >> 1) ++#define PENDING_MARGINAL_COST(p) ((unsigned int)(1 << (p))) ++#define MAX_QUEUED_EVENTS 2048 ++#define AVERAGE_EVENT_SIZE sizeof (struct inotify_event) + 16 ++#define TIMEOUT_MILLISECONDS 10 ++static gboolean ++ik_source_check (GSource *source) ++{ ++ static int prev_pending = 0, pending_count = 0; ++ ++ /* We already disabled the PollFD or ++ * nothing to be read from inotify */ ++ if (!ik_poll_fd_enabled || !(ik_poll_fd.revents & G_IO_IN)) ++ { ++ return FALSE; ++ } ++ ++ if (pending_count < MAX_PENDING_COUNT) { ++ unsigned int pending; ++ ++ if (ioctl (inotify_instance_fd, FIONREAD, &pending) == -1) ++ goto do_read; ++ ++ pending /= AVERAGE_EVENT_SIZE; ++ ++ /* Don't wait if the number of pending events is too close ++ * to the maximum queue size. ++ */ ++ if (pending > PENDING_THRESHOLD (MAX_QUEUED_EVENTS)) ++ goto do_read; ++ ++ /* With each successive iteration, the minimum rate for ++ * further sleep doubles. */ ++ if (pending-prev_pending < PENDING_MARGINAL_COST(pending_count)) ++ goto do_read; ++ ++ prev_pending = pending; ++ pending_count++; ++ ++ /* We are going to wait to read the events: */ ++ ++ /* Remove the PollFD from the source */ ++ g_source_remove_poll (source, &ik_poll_fd); ++ /* To avoid threading issues we need to flag that we've done that */ ++ ik_poll_fd_enabled = FALSE; ++ /* Set a timeout to re-add the PollFD to the source */ ++ g_source_ref (source); ++ g_timeout_add (TIMEOUT_MILLISECONDS, ik_source_timeout, source); ++ ++ return FALSE; ++ } ++ ++do_read: ++ /* We are ready to read events from inotify */ ++ ++ prev_pending = 0; ++ pending_count = 0; ++ ++ return TRUE; ++} ++ ++static gboolean ++ik_source_dispatch (GSource *source, ++ GSourceFunc callback, ++ gpointer user_data) ++{ ++ if (callback) ++ { ++ return callback(user_data); ++ } ++ return TRUE; ++} ++ ++GSourceFuncs ik_source_funcs = ++{ ++ ik_source_prepare, ++ ik_source_check, ++ ik_source_dispatch, ++ NULL ++}; ++ ++gboolean ik_startup (void (*cb)(ik_event_t *event)) ++{ ++ static gboolean initialized = FALSE; ++ GSource *source; ++ ++ user_cb = cb; ++ /* Ignore multi-calls */ ++ if (initialized) { ++ return inotify_instance_fd >= 0; ++ } ++ ++ initialized = TRUE; ++ inotify_instance_fd = inotify_init (); ++ ++ if (inotify_instance_fd < 0) { ++ return FALSE; ++ } ++ ++ inotify_read_ioc = g_io_channel_unix_new(inotify_instance_fd); ++ ik_poll_fd.fd = inotify_instance_fd; ++ ik_poll_fd.events = G_IO_IN | G_IO_HUP | G_IO_ERR; ++ g_io_channel_set_encoding(inotify_read_ioc, NULL, NULL); ++ g_io_channel_set_flags(inotify_read_ioc, G_IO_FLAG_NONBLOCK, NULL); ++ ++ source = g_source_new (&ik_source_funcs, sizeof(GSource)); ++ g_source_add_poll (source, &ik_poll_fd); ++ g_source_set_callback(source, ik_read_callback, NULL, NULL); ++ g_source_attach(source, NULL); ++ g_source_unref (source); ++ ++ cookie_hash = g_hash_table_new(g_direct_hash, g_direct_equal); ++ event_queue = g_queue_new (); ++ events_to_process = g_queue_new (); ++ ++ return TRUE; ++} ++ ++static ik_event_internal_t *ik_event_internal_new (ik_event_t *event) ++{ ++ ik_event_internal_t *internal_event = g_new0(ik_event_internal_t, 1); ++ GTimeVal tv; ++ ++ g_assert (event); ++ ++ g_get_current_time (&tv); ++ g_time_val_add (&tv, DEFAULT_HOLD_UNTIL_TIME); ++ internal_event->event = event; ++ internal_event->hold_until = tv; ++ ++ return internal_event; ++} ++ ++static ik_event_t *ik_event_new (char *buffer) ++{ ++ struct inotify_event *kevent = (struct inotify_event *)buffer; ++ g_assert (buffer); ++ ik_event_t *event = g_new0(ik_event_t,1); ++ event->wd = kevent->wd; ++ event->mask = kevent->mask; ++ event->cookie = kevent->cookie; ++ event->len = kevent->len; ++ if (event->len) ++ event->name = g_strdup(kevent->name); ++ else ++ event->name = g_strdup(""); ++ ++ return event; ++} ++ ++ik_event_t *ik_event_new_dummy (const char *name, gint32 wd, guint32 mask) ++{ ++ ik_event_t *event = g_new0(ik_event_t,1); ++ event->wd = wd; ++ event->mask = mask; ++ event->cookie = 0; ++ if (name) ++ event->name = g_strdup(name); ++ else ++ event->name = g_strdup(""); ++ ++ event->len = strlen (event->name); ++ ++ return event; ++} ++ ++void ik_event_free (ik_event_t *event) ++{ ++ if (event->pair) ++ ik_event_free (event->pair); ++ g_free(event->name); ++ g_free(event); ++} ++ ++gint32 ik_watch (const char *path, guint32 mask, int *err) ++{ ++ gint32 wd = -1; ++ ++ g_assert (path != NULL); ++ g_assert (inotify_instance_fd >= 0); ++ ++ wd = inotify_add_watch (inotify_instance_fd, path, mask); ++ ++ if (wd < 0) ++ { ++ int e = errno; ++ // FIXME: debug msg failed to add watch ++ if (err) ++ *err = e; ++ return wd; ++ } ++ ++ g_assert (wd >= 0); ++ return wd; ++} ++ ++int ik_ignore(const char *path, gint32 wd) ++{ ++ g_assert (wd >= 0); ++ g_assert (inotify_instance_fd >= 0); ++ ++ if (inotify_rm_watch (inotify_instance_fd, wd) < 0) ++ { ++ //int e = errno; ++ // failed to rm watch ++ return -1; ++ } ++ ++ return 0; ++} ++ ++void ik_move_stats (guint32 *matches, guint32 *misses) ++{ ++ if (matches) ++ *matches = ik_move_matches; ++ ++ if (misses) ++ *misses = ik_move_misses; ++} ++ ++const char *ik_mask_to_string (guint32 mask) ++{ ++ gboolean is_dir = mask & IN_ISDIR; ++ mask &= ~IN_ISDIR; ++ ++ if (is_dir) ++ { ++ switch (mask) ++ { ++ case IN_ACCESS: ++ return "ACCESS (dir)"; ++ break; ++ case IN_MODIFY: ++ return "MODIFY (dir)"; ++ break; ++ case IN_ATTRIB: ++ return "ATTRIB (dir)"; ++ break; ++ case IN_CLOSE_WRITE: ++ return "CLOSE_WRITE (dir)"; ++ break; ++ case IN_CLOSE_NOWRITE: ++ return "CLOSE_NOWRITE (dir)"; ++ break; ++ case IN_OPEN: ++ return "OPEN (dir)"; ++ break; ++ case IN_MOVED_FROM: ++ return "MOVED_FROM (dir)"; ++ break; ++ case IN_MOVED_TO: ++ return "MOVED_TO (dir)"; ++ break; ++ case IN_DELETE: ++ return "DELETE (dir)"; ++ break; ++ case IN_CREATE: ++ return "CREATE (dir)"; ++ break; ++ case IN_DELETE_SELF: ++ return "DELETE_SELF (dir)"; ++ break; ++ case IN_UNMOUNT: ++ return "UNMOUNT (dir)"; ++ break; ++ case IN_Q_OVERFLOW: ++ return "Q_OVERFLOW (dir)"; ++ break; ++ case IN_IGNORED: ++ return "IGNORED (dir)"; ++ break; ++ default: ++ return "UNKNOWN_EVENT (dir)"; ++ break; ++ ++ } ++ } else { ++ switch (mask) ++ { ++ case IN_ACCESS: ++ return "ACCESS"; ++ break; ++ case IN_MODIFY: ++ return "MODIFY"; ++ break; ++ case IN_ATTRIB: ++ return "ATTRIB"; ++ break; ++ case IN_CLOSE_WRITE: ++ return "CLOSE_WRITE"; ++ break; ++ case IN_CLOSE_NOWRITE: ++ return "CLOSE_NOWRITE"; ++ break; ++ case IN_OPEN: ++ return "OPEN"; ++ break; ++ case IN_MOVED_FROM: ++ return "MOVED_FROM"; ++ break; ++ case IN_MOVED_TO: ++ return "MOVED_TO"; ++ break; ++ case IN_DELETE: ++ return "DELETE"; ++ break; ++ case IN_CREATE: ++ return "CREATE"; ++ break; ++ case IN_DELETE_SELF: ++ return "DELETE_SELF"; ++ break; ++ case IN_UNMOUNT: ++ return "UNMOUNT"; ++ break; ++ case IN_Q_OVERFLOW: ++ return "Q_OVERFLOW"; ++ break; ++ case IN_IGNORED: ++ return "IGNORED"; ++ break; ++ default: ++ return "UNKNOWN_EVENT"; ++ break; ++ ++ } ++ } ++} ++ ++ ++static void ik_read_events (gsize *buffer_size_out, gchar **buffer_out) ++{ ++ static gchar *buffer = NULL; ++ static gsize buffer_size; ++ ++ /* Initialize the buffer on our first call */ ++ if (buffer == NULL) ++ { ++ buffer_size = AVERAGE_EVENT_SIZE; ++ buffer_size *= MAX_QUEUED_EVENTS; ++ buffer = g_malloc (buffer_size); ++ ++ if (!buffer) { ++ *buffer_size_out = 0; ++ *buffer_out = NULL; ++ return; ++ } ++ } ++ ++ *buffer_size_out = 0; ++ *buffer_out = NULL; ++ ++ memset(buffer, 0, buffer_size); ++ ++ if (g_io_channel_read_chars (inotify_read_ioc, (char *)buffer, buffer_size, buffer_size_out, NULL) != G_IO_STATUS_NORMAL) { ++ // error reading ++ } ++ *buffer_out = buffer; ++} ++ ++static gboolean ik_read_callback(gpointer user_data) ++{ ++ gchar *buffer; ++ gsize buffer_size, buffer_i, events; ++ ++ G_LOCK(inotify_lock); ++ ik_read_events (&buffer_size, &buffer); ++ ++ buffer_i = 0; ++ events = 0; ++ while (buffer_i < buffer_size) ++ { ++ struct inotify_event *event; ++ gsize event_size; ++ event = (struct inotify_event *)&buffer[buffer_i]; ++ event_size = sizeof(struct inotify_event) + event->len; ++ g_queue_push_tail (events_to_process, ik_event_internal_new (ik_event_new (&buffer[buffer_i]))); ++ buffer_i += event_size; ++ events++; ++ } ++ ++ /* If the event process callback is off, turn it back on */ ++ if (!process_eq_running && events) ++ { ++ process_eq_running = TRUE; ++ g_timeout_add (PROCESS_EVENTS_TIME, ik_process_eq_callback, NULL); ++ } ++ ++ G_UNLOCK(inotify_lock); ++ return TRUE; ++} ++ ++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 ++g_timeval_eq(GTimeVal *val1, GTimeVal *val2) ++{ ++ return (val1->tv_sec == val2->tv_sec) && (val1->tv_usec == val2->tv_usec); ++} ++ ++static void ++ik_pair_events (ik_event_internal_t *event1, ik_event_internal_t *event2) ++{ ++ g_assert (event1 && event2); ++ /* We should only be pairing events that have the same cookie */ ++ g_assert (event1->event->cookie == event2->event->cookie); ++ /* We shouldn't pair an event that already is paired */ ++ g_assert (event1->pair == NULL && event2->pair == NULL); ++ ++ /* Pair the internal structures and the ik_event_t structures */ ++ event1->pair = event2; ++ event1->event->pair = event2->event; ++ ++ if (g_timeval_lt (&event1->hold_until, &event2->hold_until)) ++ event1->hold_until = event2->hold_until; ++ ++ event2->hold_until = event1->hold_until; ++} ++ ++static void ++ik_event_add_microseconds (ik_event_internal_t *event, glong ms) ++{ ++ g_assert (event); ++ g_time_val_add (&event->hold_until, ms); ++} ++ ++static gboolean ++ik_event_ready (ik_event_internal_t *event) ++{ ++ GTimeVal tv; ++ g_assert (event); ++ ++ g_get_current_time (&tv); ++ ++ /* An event is ready if, ++ * ++ * it has no cookie -- there is nothing to be gained by holding it ++ * or, it is already paired -- we don't need to hold it anymore ++ * or, we have held it long enough ++ */ ++ return event->event->cookie == 0 || ++ event->pair != NULL || ++ g_timeval_lt(&event->hold_until, &tv) || g_timeval_eq(&event->hold_until, &tv); ++} ++ ++static void ++ik_pair_moves (gpointer data, gpointer user_data) ++{ ++ ik_event_internal_t *event = (ik_event_internal_t *)data; ++ ++ if (event->seen == TRUE || event->sent == TRUE) ++ return; ++ ++ if (event->event->cookie != 0) ++ { ++ /* When we get a MOVED_FROM event we delay sending the event by ++ * MOVE_HOLD_UNTIL_TIME microseconds. We need to do this because a ++ * MOVED_TO pair _might_ be coming in the near future */ ++ if (event->event->mask & IN_MOVED_FROM) { ++ g_hash_table_insert (cookie_hash, GINT_TO_POINTER(event->event->cookie), event); ++ // because we don't deliver move events there is no point in waiting for the match right now. ++ ik_event_add_microseconds (event, MOVE_HOLD_UNTIL_TIME); ++ } else if (event->event->mask & IN_MOVED_TO) { ++ /* We need to check if we are waiting for this MOVED_TO events cookie to pair it with ++ * a MOVED_FROM */ ++ ik_event_internal_t *match = NULL; ++ match = g_hash_table_lookup (cookie_hash, GINT_TO_POINTER(event->event->cookie)); ++ if (match) { ++ g_hash_table_remove (cookie_hash, GINT_TO_POINTER(event->event->cookie)); ++ ik_pair_events (match, event); ++ } ++ } ++ } ++ event->seen = TRUE; ++} ++ ++static void ++ik_process_events () ++{ ++ g_queue_foreach (events_to_process, ik_pair_moves, NULL); ++ ++ while (!g_queue_is_empty (events_to_process)) ++ { ++ ik_event_internal_t *event = g_queue_peek_head (events_to_process); ++ ++ /* This must have been sent as part of a MOVED_TO/MOVED_FROM */ ++ if (event->sent) ++ { ++ /* Pop event */ ++ g_queue_pop_head (events_to_process); ++ /* Free the internal event structure */ ++ g_free (event); ++ continue; ++ } ++ ++ /* The event isn't ready yet */ ++ if (!ik_event_ready (event)) { ++ break; ++ } ++ ++ /* Pop it */ ++ event = g_queue_pop_head (events_to_process); ++ ++ /* Check if this is a MOVED_FROM that is also sitting in the cookie_hash */ ++ if (event->event->cookie && event->pair == NULL && ++ g_hash_table_lookup (cookie_hash, GINT_TO_POINTER(event->event->cookie))) ++ { ++ g_hash_table_remove (cookie_hash, GINT_TO_POINTER(event->event->cookie)); ++ } ++ ++ if (event->pair) { ++ /* We send out paired MOVED_FROM/MOVED_TO events in the same event buffer */ ++ //g_assert (event->event->mask == IN_MOVED_FROM && event->pair->event->mask == IN_MOVED_TO); ++ /* Copy the paired data */ ++ event->pair->sent = TRUE; ++ event->sent = TRUE; ++ ik_move_matches++; ++ } else if (event->event->cookie) { ++ /* If we couldn't pair a MOVED_FROM and MOVED_TO together, we change ++ * the event masks */ ++ /* Changeing MOVED_FROM to DELETE and MOVED_TO to create lets us make ++ * the gaurantee that you will never see a non-matched MOVE event */ ++ ++ if (event->event->mask & IN_MOVED_FROM) { ++ event->event->mask = IN_DELETE|(event->event->mask & IN_ISDIR); ++ ik_move_misses++; // not super accurate, if we aren't watching the destination it still counts as a miss ++ } ++ if (event->event->mask & IN_MOVED_TO) ++ event->event->mask = IN_CREATE|(event->event->mask & IN_ISDIR); ++ } ++ ++ /* Push the ik_event_t onto the event queue */ ++ g_queue_push_tail (event_queue, event->event); ++ /* Free the internal event structure */ ++ g_free (event); ++ } ++} ++ ++gboolean ik_process_eq_callback (gpointer user_data) ++{ ++ /* Try and move as many events to the event queue */ ++ G_LOCK(inotify_lock); ++ ik_process_events (); ++ ++ while (!g_queue_is_empty (event_queue)) ++ { ++ ik_event_t *event = g_queue_pop_head (event_queue); ++ ++ user_cb (event); ++ } ++ ++ if (g_queue_get_length (events_to_process) == 0) ++ { ++ process_eq_running = FALSE; ++ G_UNLOCK(inotify_lock); ++ return FALSE; ++ } else { ++ G_UNLOCK(inotify_lock); ++ return TRUE; ++ } ++} +--- /dev/null 2006-08-28 15:22:40.902752500 +0200 ++++ gamin-0.1.7/server/inotify-missing.c 2006-09-05 11:01:21.000000000 +0200 +@@ -0,0 +1,158 @@ ++/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ ++ ++/* inotify-helper.c - Gnome VFS Monitor based on inotify. ++ ++ Copyright (C) 2005 John McCutchan ++ ++ The Gnome Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Library General Public License as ++ published by the Free Software Foundation; either version 2 of the ++ License, or (at your option) any later version. ++ ++ The Gnome 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 ++ Library General Public License for more details. ++ ++ You should have received a copy of the GNU Library General Public ++ License along with the Gnome Library; see the file COPYING.LIB. If not, ++ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, ++ Boston, MA 02111-1307, USA. ++ ++ Authors: ++ John McCutchan ++*/ ++ ++#include "config.h" ++#include ++#include "inotify-missing.h" ++#include "inotify-path.h" ++ ++#define SCAN_MISSING_TIME 4000 /* 1/4 Hz */ ++ ++static gboolean im_debug_enabled = FALSE; ++#define IM_W if (im_debug_enabled) g_warning ++ ++/* We put ih_sub_t's that are missing on this list */ ++static GList *missing_sub_list = NULL; ++static gboolean im_scan_missing (gpointer user_data); ++static gboolean scan_missing_running = FALSE; ++static void (*missing_cb)(ih_sub_t *sub) = NULL; ++ ++G_LOCK_EXTERN (inotify_lock); ++ ++/* inotify_lock must be held before calling */ ++void im_startup (void (*callback)(ih_sub_t *sub)) ++{ ++ static gboolean initialized = FALSE; ++ ++ if (!initialized) { ++ initialized = TRUE; ++ missing_cb = callback; ++ } ++} ++ ++/* inotify_lock must be held before calling */ ++void im_add (ih_sub_t *sub) ++{ ++ if (g_list_find (missing_sub_list, sub)) { ++ IM_W("asked to add %s to missing list but it's already on the list!\n", sub->pathname); ++ return; ++ } ++ ++ IM_W("adding %s to missing list\n", sub->dirname); ++ missing_sub_list = g_list_prepend (missing_sub_list, sub); ++ ++ /* If the timeout is turned off, we turn it back on */ ++ if (!scan_missing_running) ++ { ++ scan_missing_running = TRUE; ++ g_timeout_add (SCAN_MISSING_TIME, im_scan_missing, NULL); ++ } ++} ++ ++/* inotify_lock must be held before calling */ ++void im_rm (ih_sub_t *sub) ++{ ++ GList *link; ++ ++ link = g_list_find (missing_sub_list, sub); ++ ++ if (!link) { ++ IM_W("asked to remove %s from missing list but it isn't on the list!\n", sub->pathname); ++ return; ++ } ++ ++ IM_W("removing %s from missing list\n", sub->dirname); ++ ++ missing_sub_list = g_list_remove_link (missing_sub_list, link); ++ g_list_free_1 (link); ++} ++ ++/* Scans the list of missing subscriptions checking if they ++ * are available yet. ++ */ ++static gboolean im_scan_missing (gpointer user_data) ++{ ++ GList *nolonger_missing = NULL; ++ GList *l; ++ ++ G_LOCK(inotify_lock); ++ ++ IM_W("scanning missing list with %d items\n", g_list_length (missing_sub_list)); ++ for (l = missing_sub_list; l; l = l->next) ++ { ++ ih_sub_t *sub = l->data; ++ gboolean not_m = FALSE; ++ ++ IM_W("checking %p\n", sub); ++ g_assert (sub); ++ g_assert (sub->dirname); ++ not_m = ip_start_watching (sub); ++ ++ if (not_m) ++ { ++ missing_cb (sub); ++ IM_W("removed %s from missing list\n", sub->dirname); ++ /* We have to build a list of list nodes to remove from the ++ * missing_sub_list. We do the removal outside of this loop. ++ */ ++ nolonger_missing = g_list_prepend (nolonger_missing, l); ++ } ++ } ++ ++ for (l = nolonger_missing; l ; l = l->next) ++ { ++ GList *llink = l->data; ++ missing_sub_list = g_list_remove_link (missing_sub_list, llink); ++ g_list_free_1 (llink); ++ } ++ ++ g_list_free (nolonger_missing); ++ ++ /* If the missing list is now empty, we disable the timeout */ ++ if (missing_sub_list == NULL) ++ { ++ scan_missing_running = FALSE; ++ G_UNLOCK(inotify_lock); ++ return FALSE; ++ } else { ++ G_UNLOCK(inotify_lock); ++ return TRUE; ++ } ++} ++ ++ ++/* inotify_lock must be held */ ++void ++im_diag_dump (GIOChannel *ioc) ++{ ++ GList *l; ++ g_io_channel_write_chars (ioc, "missing list:\n", -1, NULL, NULL); ++ for (l = missing_sub_list; l; l = l->next) ++ { ++ ih_sub_t *sub = l->data; ++ g_io_channel_write_chars (ioc, sub->pathname, -1, NULL, NULL); ++ g_io_channel_write_chars (ioc, "\n", -1, NULL, NULL); ++ } ++} +--- gamin-0.1.7/server/gam_inotify.c.new-inotify-backend 2005-10-25 16:16:28.000000000 +0200 ++++ gamin-0.1.7/server/gam_inotify.c 2006-09-05 12:13:38.000000000 +0200 +@@ -17,1582 +17,201 @@ + */ + + #include "server_config.h" +-#define _GNU_SOURCE +-#include +-#include + #include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include ++/* Just include the local header to stop all the pain */ ++#include "local_inotify.h" ++#if 0 ++#ifdef HAVE_SYS_INOTIFY_H ++/* We don't actually include the libc header, because there has been ++ * problems with libc versions that was built without inotify support. ++ * Instead we use the local version. ++ */ ++#include "local_inotify.h" ++#elif defined (HAVE_LINUX_INOTIFY_H) ++#include ++#endif ++#endif ++#include "inotify-sub.h" ++#include "inotify-helper.h" ++#include "inotify-diag.h" + #ifdef GAMIN_DEBUG_API + #include "gam_debugging.h" + #endif + #include "gam_error.h" +-#include "gam_poll_basic.h" +-#ifdef HAVE_LINUX_INOTIFY_H +-#include +-#else +-#include "local_inotify.h" +-#endif +-#include "local_inotify_syscalls.h" +-#include "gam_inotify.h" +-#include "gam_tree.h" + #include "gam_event.h" + #include "gam_server.h" +-#include "gam_event.h" +-#include "gam_fs.h" +- +-#define GAM_INOTIFY_SANITY +-#define GAM_INOTIFY_WD_MISSING -1 +-#define GAM_INOTIFY_WD_PERM -2 +-#define GAM_INOTIFY_WD_LINK -3 +- +-/* Timings for pairing MOVED_TO / MOVED_FROM events */ +-/* These numbers are in microseconds */ +-#define DEFAULT_HOLD_UNTIL_TIME 1000 /* 1 ms */ +-#define MOVE_HOLD_UNTIL_TIME 5000 /* 5 ms */ +- +-/* Timings for main loop */ +-/* These numbers are in milliseconds */ +-#define SCAN_MISSING_TIME 1000 /* 1 Hz */ +-#define SCAN_LINKS_TIME 1000 /* 1 Hz */ +-#define PROCESS_EVENTS_TIME 33 /* 30 Hz */ +- +-typedef struct { +- /* The full pathname of this node */ +- char *path; +- gboolean dir; /* Is this path a directory */ +- +- /* Inotify */ +- int wd; +- +- /* State */ +- gboolean busy; +- gboolean missing; +- gboolean link; +- gboolean permission; /* Exists, but don't have read access */ +- gboolean deactivated; +- gboolean ignored; +- int refcount; +- +- /* Statistics */ +- int events; +- int deactivated_events; +- int ignored_events; +- +- /* Gamin state */ +- GList *subs; +-} inotify_data_t; +- +-typedef struct _inotify_event_t { +- gint wd; +- gint mask; +- gint cookie; +- char *name; +- gboolean seen; +- gboolean sent; +- GTimeVal hold_until; +- struct _inotify_event_t *pair; +-} inotify_event_t; +- +-typedef struct { +- char *path; +- GTime last_scan_time; +- GTime scan_interval; +- gboolean permission; +-} inotify_missing_t; +- +-typedef struct { +- char *path; +- struct stat sbuf; +- GTime last_scan_time; +- GTime scan_interval; +-} inotify_links_t; +- +-static GHashTable * path_hash = NULL; +-static GHashTable * wd_hash = NULL; +-static GList * missing_list = NULL; +-static GList * links_list = NULL; +-static GHashTable * cookie_hash = NULL; +-static GQueue * event_queue = NULL; +-static GQueue * events_to_process = NULL; +-static GIOChannel * inotify_read_ioc = NULL; +-static int inotify_device_fd = -1; +- +-#define GAM_INOTIFY_MASK (IN_MODIFY|IN_ATTRIB|IN_MOVED_FROM|IN_MOVED_TO|IN_DELETE|IN_CREATE|IN_DELETE_SELF|IN_UNMOUNT|IN_MOVE_SELF) +- +-static int gam_inotify_add_watch (const char *path, __u32 mask, int *err); +-static int gam_inotify_rm_watch (const char *path, __u32 wd); +-static void gam_inotify_read_events (gsize *buffer_size_out, gchar **buffer_out); +- +-static gboolean gam_inotify_is_missing (const char *path); +-static gboolean gam_inotify_nolonger_missing (const char *path); +-static void gam_inotify_add_missing (const char *path, gboolean perm); +-static void gam_inotify_rm_missing (const char *path); +-static gboolean gam_inotify_scan_missing (gpointer userdata); +- +-static gboolean gam_inotify_is_link (const char *path); +-static gboolean gam_inotify_nolonger_link (const char *path); +-static void gam_inotify_add_link (const char *path); +-static void gam_inotify_rm_link (const char *path); +-static gboolean gam_inotify_scan_links (gpointer userdata); +-static void gam_inotify_poll_link (inotify_links_t *links); +- +-static void gam_inotify_sanity_check (void); +- +-static gboolean g_timeval_lt (GTimeVal *val1, GTimeVal *val2); +-static gboolean g_timeval_eq (GTimeVal *val1, GTimeVal *val2); +- +-static void +-gam_inotify_data_debug (gpointer key, gpointer value, gpointer user_data) +-{ +- int busy; +- int deactivated; +- int ignored; +- int missing; +- int permission; +- inotify_data_t *data = (inotify_data_t *)value; +- +- if (!data) +- return; +- +- busy = data->busy; +- deactivated = data->deactivated; +- ignored = data->ignored; +- missing = data->missing; +- permission = data->permission; +- +- GAM_DEBUG(DEBUG_INFO, "isub wd %d refs %d permission %d missing %d busy %d deactivated %d ignored %d events (%d:%d:%d): %s\n", data->wd, data->refcount, permission, missing, busy, deactivated, ignored, data->events, data->deactivated_events, data->ignored_events, data->path); +-} +- +-gboolean +-gam_inotify_is_running(void) +-{ +- return inotify_device_fd >= 0; +-} +- +-void +-gam_inotify_debug(void) +-{ +- if (inotify_device_fd == -1) +- { +- return; +- } +- +- if (path_hash == NULL) +- return; +- +- GAM_DEBUG(DEBUG_INFO, "Inotify device fd = %d\n", inotify_device_fd); +- GAM_DEBUG(DEBUG_INFO, "Dumping inotify subscriptions\n"); +- g_hash_table_foreach (path_hash, gam_inotify_data_debug, NULL); +-} +- +-static const char * +-mask_to_string (int mask) +-{ +- mask &= ~IN_ISDIR; +- switch (mask) +- { +- case IN_ACCESS: +- return "ACCESS"; +- break; +- case IN_MODIFY: +- return "MODIFY"; +- break; +- case IN_ATTRIB: +- return "ATTRIB"; +- break; +- case IN_CLOSE_WRITE: +- return "CLOSE_WRITE"; +- break; +- case IN_CLOSE_NOWRITE: +- return "CLOSE_NOWRITE"; +- break; +- case IN_OPEN: +- return "OPEN"; +- break; +- case IN_MOVED_FROM: +- return "MOVED_FROM"; +- break; +- case IN_MOVED_TO: +- return "MOVED_TO"; +- break; +- case IN_DELETE: +- return "DELETE"; +- break; +- case IN_CREATE: +- return "CREATE"; +- break; +- case IN_DELETE_SELF: +- return "DELETE_SELF"; +- break; +- case IN_UNMOUNT: +- return "UNMOUNT"; +- break; +- case IN_Q_OVERFLOW: +- return "Q_OVERFLOW"; +- break; +- case IN_IGNORED: +- return "IGNORED"; +- break; +- default: +- return "UNKNOWN_EVENT"; +- break; +- } +-} +- +-static GaminEventType +-mask_to_gam_event (gint mask) +-{ +- mask &= ~IN_ISDIR; +- switch (mask) +- { +- case IN_MODIFY: +- case IN_ATTRIB: +- return GAMIN_EVENT_CHANGED; +- break; +- case IN_MOVE_SELF: +- case IN_MOVED_FROM: +- case IN_DELETE: +- case IN_DELETE_SELF: +- return GAMIN_EVENT_DELETED; +- break; +- case IN_CREATE: +- case IN_MOVED_TO: +- return GAMIN_EVENT_CREATED; +- break; +- case IN_Q_OVERFLOW: +- case IN_OPEN: +- case IN_CLOSE_WRITE: +- case IN_CLOSE_NOWRITE: +- case IN_UNMOUNT: +- case IN_ACCESS: +- case IN_IGNORED: +- default: +- return GAMIN_EVENT_UNKNOWN; +- break; +- } +-} +- +-/* Called when a directory is being watched as a file */ +-static GaminEventType +-gam_inotify_mask_to_gam_file_event (gint mask) +-{ +- mask &= ~IN_ISDIR; +- switch (mask) +- { +- case IN_MOVED_FROM: +- case IN_DELETE: +- case IN_CREATE: +- case IN_MOVED_TO: +- return GAMIN_EVENT_CHANGED; +- break; +- case IN_MOVE_SELF: +- case IN_DELETE_SELF: +- return GAMIN_EVENT_DELETED; +- break; +- case IN_ATTRIB: +- case IN_MODIFY: +- case IN_Q_OVERFLOW: +- case IN_OPEN: +- case IN_CLOSE_WRITE: +- case IN_CLOSE_NOWRITE: +- case IN_UNMOUNT: +- case IN_ACCESS: +- case IN_IGNORED: +- default: +- return GAMIN_EVENT_UNKNOWN; +- break; +- } +-} ++#include "gam_subscription.h" ++#include "gam_inotify.h" + +-/* Called when a file is watched as a directory */ ++/* Transforms a inotify event to a gamin event. */ + static GaminEventType +-gam_inotify_mask_to_gam_dir_event (gint mask) ++ih_mask_to_EventType (guint32 mask) + { +- mask &= ~IN_ISDIR; +- switch (mask) +- { +- case IN_MOVED_FROM: +- case IN_DELETE: +- case IN_CREATE: +- case IN_MOVED_TO: +- case IN_MOVE_SELF: +- case IN_DELETE_SELF: +- case IN_ATTRIB: +- case IN_MODIFY: +- case IN_Q_OVERFLOW: +- case IN_OPEN: +- case IN_CLOSE_WRITE: +- case IN_CLOSE_NOWRITE: +- case IN_UNMOUNT: +- case IN_ACCESS: +- case IN_IGNORED: +- default: +- return GAMIN_EVENT_UNKNOWN; ++ mask &= ~IN_ISDIR; ++ switch (mask) ++ { ++ case IN_MODIFY: ++ return GAMIN_EVENT_CHANGED; ++ break; ++ case IN_ATTRIB: ++ return GAMIN_EVENT_CHANGED; ++ break; ++ case IN_MOVE_SELF: ++ case IN_MOVED_FROM: ++ case IN_DELETE: ++ case IN_DELETE_SELF: ++ return GAMIN_EVENT_DELETED; ++ break; ++ case IN_CREATE: ++ case IN_MOVED_TO: ++ return GAMIN_EVENT_CREATED; ++ break; ++ case IN_Q_OVERFLOW: ++ case IN_OPEN: ++ case IN_CLOSE_WRITE: ++ case IN_CLOSE_NOWRITE: ++ case IN_UNMOUNT: ++ case IN_ACCESS: ++ case IN_IGNORED: ++ default: ++ return -1; + break; + } + } + +-static inotify_data_t * +-gam_inotify_data_new(const char *path, int wd, gboolean dir) +-{ +- inotify_data_t *data; +- +- data = g_new0(inotify_data_t, 1); +- +- data->path = g_strdup(path); +- data->wd = wd; +- data->busy = FALSE; +- if (wd == GAM_INOTIFY_WD_MISSING) +- data->missing = TRUE; +- else +- data->missing = FALSE; +- if (wd == GAM_INOTIFY_WD_PERM) +- data->permission = TRUE; +- else +- data->permission = FALSE; +- if (wd == GAM_INOTIFY_WD_LINK) +- data->link = TRUE; +- else +- data->link = FALSE; +- data->deactivated = FALSE; +- data->ignored = FALSE; +- data->refcount = 1; +- data->events = 0; +- data->deactivated_events = 0; +- data->ignored_events = 0; +- data->dir = dir; +- +- return data; +-} +- +-static void +-gam_inotify_data_free(inotify_data_t * data) +-{ +- if (data->refcount != 0) +- GAM_DEBUG(DEBUG_INFO, "gam_inotify_data_free called with reffed data.\n"); +- +- g_free(data->path); +- g_free(data); +-} +- +-static inotify_event_t * +-gam_inotify_event_new (struct inotify_event *event) +-{ +- inotify_event_t *gam_event; +- GTimeVal tv; +- +- gam_event = g_new0(inotify_event_t, 1); +- +- gam_event->wd = event->wd; +- gam_event->mask = event->mask; +- gam_event->cookie = event->cookie; +- +- if (event->len) +- { +- gam_event->name = g_strdup (event->name); +- } else { +- gam_event->name = g_strdup (""); +- } +- +- g_get_current_time (&tv); +- g_time_val_add (&tv, DEFAULT_HOLD_UNTIL_TIME); +- gam_event->hold_until = tv; +- +- return gam_event; +-} +- +-static void +-gam_inotify_event_free (inotify_event_t *event) +-{ +- g_free (event->name); +- g_free (event); +-} +- +-static void +-gam_inotify_event_pair_with (inotify_event_t *event1, inotify_event_t *event2) +-{ +- g_assert (event1 && event2); +- /* We should only be pairing events that have the same cookie */ +- g_assert (event1->cookie == event2->cookie); +- /* We shouldn't pair an event that already is paired */ +- g_assert (event1->pair == NULL && event2->pair == NULL); +- event1->pair = event2; +- event2->pair = event1; +- +- GAM_DEBUG(DEBUG_INFO, "inotify: pairing a MOVE together\n"); +- if (g_timeval_lt (&event1->hold_until, &event2->hold_until)) +- event1->hold_until = event2->hold_until; +- +- event2->hold_until = event1->hold_until; +-} +- +-static void +-gam_inotify_event_add_microseconds (inotify_event_t *event, glong ms) +-{ +- g_assert (event); +- g_time_val_add (&event->hold_until, ms); +-} +- +-static gboolean +-gam_inotify_event_ready (inotify_event_t *event) +-{ +- GTimeVal tv; +- g_assert (event); +- +- g_get_current_time (&tv); +- +- /* An event is ready if, +- * +- * it has no cookie -- there is nothing to be gained by holding it +- * or, it is already paired -- we don't need to hold it anymore +- * or, we have held it long enough +- */ +- return event->cookie == 0 || +- event->pair != NULL || +- g_timeval_lt(&event->hold_until, &tv) || g_timeval_eq(&event->hold_until, &tv); +-} +- + static void +-gam_inotify_emit_one_event (inotify_data_t *data, inotify_event_t *event, GamSubscription *sub) ++gam_inotify_send_initial_events (const char *pathname, GamSubscription *sub, gboolean is_dir, gboolean was_missing) + { +- gint force = 1; +- gint is_dir_node = 0; + GaminEventType gevent; +- gchar *fullpath = NULL; +- gboolean watching_dir_as_file; +- gboolean watching_file_as_dir; + +- g_assert (data && event); ++ gevent = was_missing ? GAMIN_EVENT_CREATED : GAMIN_EVENT_EXISTS; + +- is_dir_node = event->mask & IN_ISDIR; +- watching_dir_as_file = data->dir && !gam_subscription_is_dir (sub); +- watching_file_as_dir = !data->dir && gam_subscription_is_dir (sub); ++ gam_server_emit_one_event (pathname, is_dir ? 1 : 0, gevent, sub, 1); + +- if (watching_dir_as_file) ++ if (is_dir) + { +- gevent = gam_inotify_mask_to_gam_file_event (event->mask); +- fullpath = g_strdup (data->path); +- } else if (watching_file_as_dir) { +- gevent = gam_inotify_mask_to_gam_dir_event (event->mask); +- fullpath = g_strdup (data->path); +- } else { +- gevent = mask_to_gam_event (event->mask); +- if (strlen (event->name) == 0) +- fullpath = g_strdup (data->path); +- else +- fullpath = g_strdup_printf ("%s/%s", data->path, event->name); +- } +- +- if (gevent == GAMIN_EVENT_UNKNOWN) { +- GAM_DEBUG(DEBUG_INFO, "inotify: Not handling event %d\n", event->mask); +- g_free (fullpath); +- return; +- } +- +- GAM_DEBUG(DEBUG_INFO, "inotify: Emitting %s on %s\n", gam_event_to_string (gevent), fullpath); +- gam_server_emit_one_event (fullpath, is_dir_node, gevent, sub, force); +- g_free(fullpath); +-} +- +-static void +-gam_inotify_emit_events (inotify_data_t *data, inotify_event_t *event) +-{ +- GList *l; +- +- if (!data||!event) +- return; +- +- for (l = data->subs; l; l = l->next) { +- GamSubscription *sub = l->data; +- gam_inotify_emit_one_event (data, event, sub); +- } +-} +- +-static void +-gam_inotify_process_event (inotify_event_t *event) +-{ +- inotify_data_t *data = NULL; +- +- data = g_hash_table_lookup (wd_hash, GINT_TO_POINTER(event->wd)); +- +- if (!data) +- { +- GAM_DEBUG (DEBUG_INFO, "inotify: got %s event for unknown wd %d\n", mask_to_string (event->mask), event->wd); +- return; +- } +- +- if (data->deactivated) +- { +- GAM_DEBUG (DEBUG_INFO, "inotify: ignoring event on temporarily deactivated watch %s\n", data->path); +- data->deactivated_events++; +- return; +- } +- +- if (data->ignored) { +- GAM_DEBUG (DEBUG_INFO, "inotify: got event on ignored watch %s\n", data->path); +- data->ignored_events++; +- return; +- } +- +- if (event->mask & IN_IGNORED) +- { +- data->ignored = TRUE; +- data->ignored_events++; +- return; +- } +- +- if (event->mask & IN_DELETE_SELF || event->mask & IN_MOVE_SELF) +- { +- GAM_DEBUG (DEBUG_INFO, "inotify: resource %s went away. Adding it to missing list\n", data->path); +- /* Remove the wd from the hash table */ +- g_hash_table_remove (wd_hash, GINT_TO_POINTER(data->wd)); +-#ifdef GAMIN_DEBUG_API +- gam_debug_report(GAMDnotifyDelete, data->path, 0); +-#endif +- /* Send delete event */ +- gam_inotify_emit_events (data, event); +- data->events++; +- /* Set state bits in struct */ +- data->wd = GAM_INOTIFY_WD_MISSING; +- data->missing = TRUE; +- data->permission = FALSE; +- data->dir = FALSE; +- /* Add path to missing list */ +- gam_inotify_add_missing (data->path, FALSE); +- return; +- } +- +- if (event->mask & GAM_INOTIFY_MASK) +- { +- GAM_DEBUG (DEBUG_INFO, "inotify: got %s on = %s/%s\n", mask_to_string (event->mask), data->path, event->name); +- gam_inotify_emit_events (data, event); +- data->events++; +- return; +- } +- +- if (event->mask & IN_Q_OVERFLOW) +- { +- /* At this point we have missed some events, and no longer have a consistent +- * view of the filesystem. +- */ +- // XXX: Kill server and hope for the best? +- // XXX: Or we could send_initial_events , does this work for FAM? +- GAM_DEBUG (DEBUG_INFO, "inotify: DANGER, queue over flowed! Events have been missed.\n"); +- return; +- } +- +- GAM_DEBUG(DEBUG_INFO, "inotify: error event->mask = %d\n", event->mask); +-} +- +-static void +-gam_inotify_pair_moves (gpointer data, gpointer user_data) +-{ +- inotify_event_t *event = (inotify_event_t *)data; +- +- if (event->seen == TRUE || event->sent == TRUE) +- return; ++ GDir *dir; ++ GError *err = NULL; ++ dir = g_dir_open (pathname, 0, &err); ++ if (dir) ++ { ++ const char *filename; + +- if (event->cookie != 0) +- { +- if (event->mask & IN_MOVED_FROM) { +- g_hash_table_insert (cookie_hash, GINT_TO_POINTER(event->cookie), event); +- gam_inotify_event_add_microseconds (event, MOVE_HOLD_UNTIL_TIME); +- } else if (event->mask & IN_MOVED_TO) { +- inotify_event_t *match = NULL; +- match = g_hash_table_lookup (cookie_hash, GINT_TO_POINTER(event->cookie)); +- if (match) { +- g_hash_table_remove (cookie_hash, GINT_TO_POINTER(event->cookie)); +- gam_inotify_event_pair_with (match, event); ++ while ((filename = g_dir_read_name (dir))) ++ { ++ gchar *fullname = g_strdup_printf ("%s/%s", pathname, filename); ++ gboolean file_is_dir = FALSE; ++ struct stat fsb; ++ memset(&fsb, 0, sizeof (struct stat)); ++ lstat(fullname, &fsb); ++ file_is_dir = (fsb.st_mode & S_IFDIR) != 0 ? TRUE : FALSE; ++ gam_server_emit_one_event (fullname, file_is_dir ? 1 : 0, gevent, sub, 1); ++ g_free (fullname); + } +- } +- } +- event->seen = TRUE; +-} +- +-static void +-gam_inotify_process_internal () +-{ +- int ecount = 0; +- g_queue_foreach (events_to_process, gam_inotify_pair_moves, NULL); + +- while (!g_queue_is_empty (events_to_process)) +- { +- inotify_event_t *event = g_queue_peek_head (events_to_process); +- +- if (!gam_inotify_event_ready (event)) { +- GAM_DEBUG(DEBUG_INFO, "inotify: event not ready\n"); +- break; +- } +- +- /* Pop it */ +- event = g_queue_pop_head (events_to_process); +- /* This must have been sent as part of a MOVED_TO/MOVED_FROM */ +- if (event->sent) +- continue; +- +- /* Check if this is a MOVED_FROM that is also sitting in the cookie_hash */ +- if (event->cookie && event->pair == NULL && +- g_hash_table_lookup (cookie_hash, GINT_TO_POINTER(event->cookie))) +- { +- g_hash_table_remove (cookie_hash, GINT_TO_POINTER(event->cookie)); +- event->sent = TRUE; +- } +- +- g_queue_push_tail (event_queue, event); +- ecount++; +- if (event->pair) { +- // if this event has a pair +- event->pair->sent = TRUE; +- g_queue_push_tail (event_queue, event->pair); +- ecount++; ++ g_dir_close (dir); ++ } else { ++ GAM_DEBUG (DEBUG_INFO, "unable to open directory %s: %s\n", pathname, err->message); ++ g_error_free (err); + } + + } +- if (ecount) +- GAM_DEBUG(DEBUG_INFO, "inotify: moved %d events to event queue\n", ecount); +-} + +-static gboolean +-gam_inotify_process_event_queue (gpointer data) +-{ +- /* Try and move as many events to the event queue */ +- gam_inotify_process_internal (); +- +- /* Send the events on the event queue to gam clients */ +- while (!g_queue_is_empty (event_queue)) ++ if (!was_missing) + { +- inotify_event_t *event = g_queue_pop_head (event_queue); +- g_assert (event); +- gam_inotify_process_event (event); +- gam_inotify_event_free (event); ++ gam_server_emit_one_event (pathname, is_dir ? 1 : 0, GAMIN_EVENT_ENDEXISTS, sub, 1); + } + +- return TRUE; +-} +- +-static gboolean +-gam_inotify_read_handler(gpointer user_data) +-{ +- gchar *buffer; +- gsize buffer_size, buffer_i, events; +- +- gam_inotify_read_events (&buffer_size, &buffer); +- +- buffer_i = 0; +- events = 0; +- while (buffer_i < buffer_size) +- { +- struct inotify_event *event; +- gsize event_size; +- event = (struct inotify_event *)&buffer[buffer_i]; +- event_size = sizeof(struct inotify_event) + event->len; +- g_queue_push_tail (events_to_process, gam_inotify_event_new (event)); +- buffer_i += event_size; +- events++; +- } +- +- GAM_DEBUG(DEBUG_INFO, "inotify recieved %d events\n", events); +- return TRUE; + } + + static void +-gam_inotify_send_initial_events (inotify_data_t *data, GamSubscription *sub) ++gam_inotify_event_callback (const char *fullpath, guint32 mask, void *subdata) + { ++ GamSubscription *sub = (GamSubscription *)subdata; + GaminEventType gevent; +- gboolean is_dir = FALSE; +- gboolean was_missing = data->missing; +- gboolean was_permission = data->permission; +- gboolean exists = FALSE; +-#if 0 +- gboolean watching_dir_as_file = data->dir && !gam_subscription_is_dir (sub); +-#endif +- gboolean watching_file_as_dir = FALSE; +- +- struct stat sb; +- memset(&sb, 0, sizeof (struct stat)); +- +- exists = lstat (data->path, &sb) >= 0; +- is_dir = (exists && (sb.st_mode & S_IFDIR) != 0) ? TRUE : FALSE; +- +- if (was_missing) { +- GAM_DEBUG (DEBUG_INFO, "inotify: Sending initial events for %s -- WAS_MISSING\n", data->path); +- } else if (was_permission) { +- GAM_DEBUG (DEBUG_INFO, "inotify: Sending initial events for %s -- WAS_PERMISSION\n", data->path); +- } else { +- GAM_DEBUG (DEBUG_INFO, "inotify: Sending initial events for %s\n", data->path); +- } +- +- if (data->wd >= 0) +- watching_file_as_dir = !data->dir && gam_subscription_is_dir (sub); +- else +- watching_file_as_dir = FALSE; +- +- if (!watching_file_as_dir && exists) +- { +- gevent = was_permission ? GAMIN_EVENT_EXISTS : was_missing ? GAMIN_EVENT_CREATED : GAMIN_EVENT_EXISTS; +- +- gam_server_emit_one_event (data->path, is_dir ? 1 : 0, gevent, sub, 1); + +- if (is_dir) +- { +- GDir *dir; +- GError *err = NULL; +- dir = g_dir_open (data->path, 0, &err); +- if (dir) +- { +- const char *filename; +- +- while ((filename = g_dir_read_name (dir))) +- { +- gchar *fullname = g_strdup_printf ("%s/%s", data->path, filename); +- gboolean file_is_dir = FALSE; +- struct stat fsb; +- memset(&fsb, 0, sizeof (struct stat)); +- lstat(fullname, &fsb); +- file_is_dir = (fsb.st_mode & S_IFDIR) != 0 ? TRUE : FALSE; +- gam_server_emit_one_event (fullname, file_is_dir ? 1 : 0, gevent, sub, 1); +- g_free (fullname); +- } +- +- g_dir_close (dir); +- } else { +- GAM_DEBUG (DEBUG_INFO, "unable to open directory %s: %s\n", data->path, err->message); +- g_error_free (err); +- } ++ gevent = ih_mask_to_EventType (mask); + +- } +- +- if (!was_missing) +- { +- gam_server_emit_one_event (data->path, is_dir ? 1 : 0, GAMIN_EVENT_ENDEXISTS, sub, 1); +- } +- +- } else { +- gam_server_emit_one_event (data->path, is_dir ? 1 : 0, GAMIN_EVENT_DELETED, sub, 1); +- gam_server_emit_one_event (data->path, is_dir ? 1 : 0, GAMIN_EVENT_ENDEXISTS, sub, 1); +- } ++ gam_server_emit_one_event (fullpath, gam_subscription_is_dir (sub), gevent, sub, 1); + } + + static void +-gam_inotify_send_initial_events_all (inotify_data_t *data) +-{ +- GList *l; +- +- if (!data) +- return; +- +- for (l = data->subs; l; l = l->next) { +- GamSubscription *sub = l->data; +- gam_inotify_send_initial_events (data, sub); +- } +- +-} +- +-/** +- * Adds a subscription to be monitored. +- * +- * @param sub a #GamSubscription to be polled +- * @returns TRUE if adding the subscription succeeded, FALSE otherwise +- */ +-gboolean +-gam_inotify_add_subscription(GamSubscription * sub) +-{ +- const char *path = gam_subscription_get_path (sub); +- inotify_data_t *data = g_hash_table_lookup (path_hash, path); +- int wd, err; +- +- +- if (data) +- { +- data->subs = g_list_prepend (data->subs, sub); +- data->refcount++; +- gam_inotify_send_initial_events (data, sub); +-#ifdef GAMIN_DEBUG_API +- gam_debug_report(GAMDnotifyChange, path, data->refcount); +-#endif +- gam_listener_add_subscription(gam_subscription_get_listener(sub), sub); +- return TRUE; +- } +- +- wd = gam_inotify_add_watch (path, GAM_INOTIFY_MASK, &err); +- if (wd < 0) { +- GAM_DEBUG (DEBUG_INFO, "inotify: could not add watch for %s\n", path); +- if (err == EACCES) { +- GAM_DEBUG (DEBUG_INFO, "inotify: adding %s to missing list PERM\n", path); +- } else { +- GAM_DEBUG (DEBUG_INFO, "inotify: adding %s to missing list MISSING\n", path); +- } +- data = gam_inotify_data_new (path, err == EACCES ? GAM_INOTIFY_WD_PERM : GAM_INOTIFY_WD_MISSING, FALSE); +- gam_inotify_add_missing (path, err == EACCES ? TRUE : FALSE); +- } else if (gam_inotify_is_link (path)) { +- /* The file turned out to be a link, cancel the watch, and add it to the links list */ +- gam_inotify_rm_watch (path, wd); +- GAM_DEBUG (DEBUG_INFO, "inotify: could not add watch for %s\n", path); +- GAM_DEBUG (DEBUG_INFO, "inotify: adding %s to links list\n", path); +- data = gam_inotify_data_new (path, GAM_INOTIFY_WD_LINK, FALSE); +- gam_inotify_add_link (path); +- } else { +- struct stat sbuf; +- memset(&sbuf, 0, sizeof (struct stat)); +- lstat (path, &sbuf); +- /* Just in case, +- * Clear this path off the missing list */ +- gam_inotify_rm_missing (path); +- data = gam_inotify_data_new (path, wd, sbuf.st_mode & S_IFDIR); +- g_hash_table_insert(wd_hash, GINT_TO_POINTER(data->wd), data); +- } +- +-#ifdef GAMIN_DEBUG_API +- gam_debug_report(GAMDnotifyCreate, path, 0); +-#endif +- gam_listener_add_subscription(gam_subscription_get_listener(sub), sub); +- +- g_hash_table_insert(path_hash, data->path, data); +- data->subs = g_list_prepend (data->subs, sub); +- gam_inotify_send_initial_events (data, sub); +- return TRUE; +-} +- +-/** +- * Removes a subscription which was being monitored. +- * +- * @param sub a #GamSubscription to remove +- * @returns TRUE if removing the subscription succeeded, FALSE otherwise +- */ +-gboolean +-gam_inotify_remove_subscription(GamSubscription * sub) ++gam_inotify_found_callback (const char *fullpath, void *subdata) + { +- const char *path = gam_subscription_get_path (sub); +- inotify_data_t *data = g_hash_table_lookup (path_hash, path); +- +- g_assert (g_list_find (data->subs, sub)); +- +- data->subs = g_list_remove_all (data->subs, sub); +- data->refcount--; +- /* No one is watching this path anymore */ +- if (!data->subs && data->refcount == 0) +- { +- if (data->link) +- { +- g_assert (data->wd == GAM_INOTIFY_WD_LINK); +- g_assert (data->missing == FALSE && data->permission == FALSE); +- g_hash_table_remove (path_hash, data->path); +- gam_inotify_rm_link (data->path); +- } else if (data->missing) { +- g_assert (data->wd == GAM_INOTIFY_WD_MISSING); +- g_assert (data->link == FALSE && data->permission == FALSE); +- g_hash_table_remove (path_hash, data->path); +- gam_inotify_rm_missing (data->path); +- } else if (data->permission) { +- g_assert (data->wd == GAM_INOTIFY_WD_PERM); +- g_assert (data->link == FALSE && data->missing == FALSE); +- g_hash_table_remove (path_hash, data->path); +- gam_inotify_rm_missing (data->path); +- } else { +- g_hash_table_remove (wd_hash, GINT_TO_POINTER(data->wd)); +- g_hash_table_remove (path_hash, data->path); +- gam_inotify_rm_watch (data->path, data->wd); +- } +-#ifdef GAMIN_DEBUG_API +- gam_debug_report(GAMDnotifyDelete, path, 0); +-#endif +- gam_inotify_data_free (data); +- } else { +-#ifdef GAMIN_DEBUG_API +- gam_debug_report(GAMDnotifyChange, path, data->refcount); +-#endif +- } +- +- gam_subscription_cancel (sub); ++ GamSubscription *sub = (GamSubscription *)subdata; + +- return TRUE; ++ gam_inotify_send_initial_events (gam_subscription_get_path (sub), sub, gam_subscription_is_dir (sub), TRUE); + } + +-/** +- * Stop monitoring all subscriptions for a given listener. +- * +- * @param listener a #GamListener +- * @returns TRUE if removing the subscriptions succeeded, FALSE otherwise +- */ +-gboolean +-gam_inotify_remove_all_for(GamListener * listener) +-{ +- GList *subs; +- GList *l; +- gboolean success = TRUE; +- +- subs = gam_listener_get_subscriptions(listener); +- +- for (l = subs; l != NULL; l = l->next) +- if (!gam_inotify_remove_subscription(l->data)) +- success = FALSE; + +- g_list_free(subs); +- +- return success; +-} +- +-/** +- * Initializes the inotify backend. This must be called before +- * any other functions in this module. +- * +- * @returns TRUE if initialization succeeded, FALSE otherwise +- */ + gboolean +-gam_inotify_init(void) ++gam_inotify_init (void) + { +- GSource *source; +- +- inotify_device_fd = inotify_init (); +- +- if (inotify_device_fd < 0) { +- GAM_DEBUG(DEBUG_INFO, "Could not initialize inotify\n"); +- return FALSE; +- } +- +- inotify_read_ioc = g_io_channel_unix_new(inotify_device_fd); +- +- g_io_channel_set_encoding(inotify_read_ioc, NULL, NULL); +- g_io_channel_set_flags(inotify_read_ioc, G_IO_FLAG_NONBLOCK, NULL); +- +- source = g_io_create_watch(inotify_read_ioc, +- G_IO_IN | G_IO_HUP | G_IO_ERR); +- g_source_set_callback(source, gam_inotify_read_handler, NULL, NULL); +- g_source_attach(source, NULL); +- g_source_unref (source); +- g_timeout_add (SCAN_MISSING_TIME, gam_inotify_scan_missing, NULL); +- g_timeout_add (SCAN_LINKS_TIME, gam_inotify_scan_links, NULL); +- g_timeout_add (PROCESS_EVENTS_TIME, gam_inotify_process_event_queue, NULL); +- +- path_hash = g_hash_table_new(g_str_hash, g_str_equal); +- wd_hash = g_hash_table_new(g_direct_hash, g_direct_equal); +- cookie_hash = g_hash_table_new(g_direct_hash, g_direct_equal); +- event_queue = g_queue_new (); +- events_to_process = g_queue_new (); +- +- gam_poll_basic_init (); +- gam_server_install_kernel_hooks (GAMIN_K_INOTIFY2, ++ gam_server_install_kernel_hooks (GAMIN_K_INOTIFY2, + gam_inotify_add_subscription, + gam_inotify_remove_subscription, +- gam_inotify_remove_all_for, NULL, NULL); +- +- GAM_DEBUG(DEBUG_INFO, "inotify backend initialized\n"); +- +- +- return TRUE; +-} +- +-int gam_inotify_add_watch (const char *path, __u32 mask, int *err) +-{ +- int wd = -1; +- +- g_assert (path != NULL); +- g_assert (inotify_device_fd >= 0); +- +- wd = inotify_add_watch (inotify_device_fd, path, mask); +- +- if (wd < 0) +- { +- int e = errno; +- GAM_DEBUG (DEBUG_INFO, "inotify: failed to add watch for %s\n", path); +- GAM_DEBUG (DEBUG_INFO, "inotify: reason %d = %s\n", e, strerror (e)); +- if (err) +- *err = e; +- return wd; +- } +- else +- { +- GAM_DEBUG (DEBUG_INFO, "inotify: success adding watch for %s (wd = %d)\n", path, wd); +- } +- +- g_assert (wd >= 0); +- +- return wd; +-} +- +-int gam_inotify_rm_watch (const char *path, __u32 wd) +-{ +- g_assert (wd >= 0); +- +- if (inotify_rm_watch (inotify_device_fd, wd) < 0) +- { +- int e = errno; +- GAM_DEBUG (DEBUG_INFO, "inotify: failed to rm watch for %s (wd = %d)\n", path, wd); +- GAM_DEBUG (DEBUG_INFO, "inotify: reason = %s\n", strerror (e)); +- return -1; +- } +- else +- { +- GAM_DEBUG (DEBUG_INFO, "inotify: success removing watch for %s (wd = %d)\n", path, wd); +- } +- +- return 0; +-} +- +-/* Code below based on beagle inotify glue code. I assume it was written by Robert Love */ +-#define MAX_PENDING_COUNT 5 +-#define PENDING_THRESHOLD(qsize) ((qsize) >> 1) +-#define PENDING_MARGINAL_COST(p) ((unsigned int)(1 << (p))) +-#define MAX_QUEUED_EVENTS 8192 +-#define AVERAGE_EVENT_SIZE sizeof (struct inotify_event) + 16 +-#define PENDING_PAUSE_MICROSECONDS 8000 +- +-void gam_inotify_read_events (gsize *buffer_size_out, gchar **buffer_out) +-{ +- static int prev_pending = 0, pending_count = 0; +- static gchar *buffer = NULL; +- static gsize buffer_size; +- +- +- /* Initialize the buffer on our first read() */ +- if (buffer == NULL) +- { +- buffer_size = AVERAGE_EVENT_SIZE; +- buffer_size *= MAX_QUEUED_EVENTS; +- buffer = g_malloc (buffer_size); +- +- if (!buffer) { +- *buffer_size_out = 0; +- *buffer_out = NULL; +- GAM_DEBUG (DEBUG_INFO, "inotify: could not allocate read buffer\n"); +- return; +- } +- } +- +- *buffer_size_out = 0; +- *buffer_out = NULL; +- +- while (pending_count < MAX_PENDING_COUNT) { +- unsigned int pending; +- +- if (ioctl (inotify_device_fd, FIONREAD, &pending) == -1) +- break; +- +- pending /= AVERAGE_EVENT_SIZE; +- +- /* Don't wait if the number of pending events is too close +- * to the maximum queue size. +- */ +- +- if (pending > PENDING_THRESHOLD (MAX_QUEUED_EVENTS)) +- break; +- +- /* With each successive iteration, the minimum rate for +- * further sleep doubles. */ +- +- if (pending-prev_pending < PENDING_MARGINAL_COST(pending_count)) +- break; +- +- prev_pending = pending; +- pending_count++; +- +- /* We sleep for a bit and try again */ +- g_usleep (PENDING_PAUSE_MICROSECONDS); +- } +- +- memset(buffer, 0, buffer_size); +- +- if (g_io_channel_read_chars (inotify_read_ioc, (char *)buffer, buffer_size, buffer_size_out, NULL) != G_IO_STATUS_NORMAL) { +- GAM_DEBUG (DEBUG_INFO, "inotify: failed to read from buffer\n"); +- } +- *buffer_out = buffer; +- +- prev_pending = 0; +- pending_count = 0; +-} +- +-gboolean gam_inotify_is_missing (const char *path) +-{ +- struct stat sbuf; +- +- /* If the file doesn't exist, it is missing. */ +- if (lstat (path, &sbuf) < 0) +- return TRUE; +- +- /* If we can't read the file, it is missing. */ +- if (access (path, R_OK) < 0) +- return TRUE; +- +- return FALSE; +-} +- +-static gint missing_list_compare (gconstpointer a, gconstpointer b) +-{ +- const inotify_missing_t *missing = NULL; ++ gam_inotify_remove_all_for, ++ NULL, NULL); + +- g_assert (a); +- g_assert (b); +- missing = a; +- g_assert (missing->path); +- +- return strcmp (missing->path, b); +-} +- +-static void gam_inotify_add_missing (const char *path, gboolean perm) +-{ +- inotify_missing_t *missing = NULL; +- +- g_assert (path); +- +- missing = g_new0 (inotify_missing_t, 1); +- +- g_assert (missing); +- +- missing->path = g_strdup (path); +- missing->scan_interval = gam_fs_get_poll_timeout (path); +- missing->last_scan_time = time (NULL); +- missing->permission = perm; +- +- GAM_DEBUG (DEBUG_INFO, "inotify-missing: add - %s\n", path); +- +- missing_list = g_list_prepend (missing_list, missing); +-} +- +-static void gam_inotify_rm_missing (const char *path) +-{ +- GList *node = NULL; +- inotify_missing_t *missing = NULL; +- +- g_assert (path && *path); +- +- node = g_list_find_custom (missing_list, path, missing_list_compare); +- +- if (!node) +- return; +- +- GAM_DEBUG (DEBUG_INFO, "inotify-missing: rm - %s\n", path); +- missing = node->data; +- g_free (missing->path); +- g_free (missing); +- +- missing_list = g_list_remove_link (missing_list, node); +-} +- +-static gboolean gam_inotify_nolonger_missing (const char *path) +-{ +- int wd = -1, err; +- inotify_data_t *data = NULL; +- struct stat sbuf; +- memset(&sbuf, 0, sizeof (struct stat)); +- +- data = g_hash_table_lookup (path_hash, path); +- if (!data) { +- GAM_DEBUG (DEBUG_INFO, "inotify: Could not find missing %s in hash table.\n", path); +- return FALSE; +- } +- +- g_assert ((data->missing == TRUE || data->permission == TRUE) && data->link == FALSE); +- +- wd = gam_inotify_add_watch (path, GAM_INOTIFY_MASK,&err); +- if (wd < 0) { +- /* Check if we don't have access to the new file */ +- if (err == EACCES) +- { +- data->wd = GAM_INOTIFY_WD_PERM; +- data->permission = TRUE; +- data->missing = FALSE; +- } else { +- data->wd = GAM_INOTIFY_WD_MISSING; +- data->permission = FALSE; +- data->missing = TRUE; +- } +- return FALSE; +- } else if (gam_inotify_is_link (path)) { +- GAM_DEBUG(DEBUG_INFO, "inotify: Missing resource %s exists now BUT IT IS A LINK\n", path); +- /* XXX: See NOTE1 */ +- if (g_hash_table_lookup (wd_hash, GINT_TO_POINTER(wd)) == NULL) +- gam_inotify_rm_watch (path, wd); +- data->missing = FALSE; +- data->permission = FALSE; +- data->link = TRUE; +- data->wd = GAM_INOTIFY_WD_LINK; +- gam_inotify_add_link (path); +- gam_inotify_send_initial_events_all (data); +- return TRUE; +- } +- +- +- GAM_DEBUG(DEBUG_INFO, "inotify: Missing resource %s exists now\n", path); +- +- lstat (path, &sbuf); +- data->dir = (sbuf.st_mode & S_IFDIR); +- data->wd = wd; +- g_hash_table_insert(wd_hash, GINT_TO_POINTER(data->wd), data); +- gam_inotify_send_initial_events_all (data); +- data->missing = FALSE; +- data->permission = FALSE; +- +- return TRUE; ++ return ih_startup (gam_inotify_event_callback, ++ gam_inotify_found_callback); + } + +-/* This function is called once per second in the main loop*/ +-static gboolean gam_inotify_scan_missing (gpointer userdata) ++gboolean ++gam_inotify_add_subscription (GamSubscription *sub) + { +- guint i; ++ ih_sub_t *isub = NULL; ++ isub = ih_sub_new (gam_subscription_get_path (sub), gam_subscription_is_dir (sub), 0, sub); + +- gam_inotify_sanity_check (); +- /* We have to walk the list like this because entries might be removed while we walk the list */ +- for (i = 0; ; i++) ++ if (!ih_sub_add (isub)) + { +- inotify_missing_t *missing = g_list_nth_data (missing_list, i); +- +- if (!missing) +- break; +- +- /* Not enough time has passed since the last scan */ +- if (time(NULL) - missing->last_scan_time < missing->scan_interval) +- continue; +- +- missing->last_scan_time = time(NULL); +- if (!gam_inotify_is_missing (missing->path)) +- { +- if (gam_inotify_nolonger_missing (missing->path)) +- { +-#ifdef GAMIN_DEBUG_API +- gam_debug_report(GAMDnotifyCreate, missing->path, 0); +-#endif +- gam_inotify_rm_missing (missing->path); +- } +- } +- } +- +- gam_inotify_sanity_check (); +- return TRUE; +-} +- +- +-static gboolean +-gam_inotify_is_link (const char *path) +-{ +- struct stat sbuf; +- +- if (lstat(path, &sbuf) < 0) +- return FALSE; +- +- return S_ISLNK(sbuf.st_mode) != 0; +-} +- +-static gboolean +-gam_inotify_nolonger_link (const char *path) +-{ +- int wd = -1, err; +- inotify_data_t *data = NULL; +- struct stat sbuf; +- memset(&sbuf, 0, sizeof (struct stat)); +- +- GAM_DEBUG(DEBUG_INFO, "inotify: link resource %s no longer a link\n", path); +- data = g_hash_table_lookup (path_hash, path); +- if (!data) { +- GAM_DEBUG (DEBUG_INFO, "inotify: Could not find link %s in hash table.\n", path); ++ ih_sub_free (isub); + return FALSE; + } + +- g_assert (data->link == TRUE && data->missing == FALSE && data->permission == FALSE); +- +- wd = gam_inotify_add_watch (path, GAM_INOTIFY_MASK, &err); +- if (wd < 0) { +- /* The file must not exist anymore, so we add it to the missing list */ +- data->link = FALSE; +- /* Check if we don't have access to the new file */ +- if (err == EACCES) +- { +- data->wd = GAM_INOTIFY_WD_PERM; +- data->permission = TRUE; +- data->missing = FALSE; +- } else { +- data->wd = GAM_INOTIFY_WD_MISSING; +- data->permission = FALSE; +- data->missing = TRUE; +- } +- +- gam_server_emit_event (path, data->dir, GAMIN_EVENT_DELETED, data->subs, 1); +- gam_inotify_add_missing (path, data->permission); +- return TRUE; +- } else if (gam_inotify_is_link (path)) { +- GAM_DEBUG(DEBUG_INFO, "inotify: Link resource %s re-appeared as a link...\n", path); +- /* NOTE1: This is tricky, because inotify works on the inode level and +- * we are dealing with a link, we can be watching the same inode +- * from two different paths (the wd's will be the same). So, +- * if the wd isn't in the hash table, we can remvoe the watch, +- * otherwise we just leave the watch. This should probably be +- * handled by ref counting +- */ +- if (g_hash_table_lookup (wd_hash, GINT_TO_POINTER(wd)) == NULL) +- gam_inotify_rm_watch (path, wd); +- data->missing = FALSE; +- data->permission = FALSE; +- data->link = TRUE; +- data->wd = GAM_INOTIFY_WD_LINK; +- gam_inotify_send_initial_events_all (data); +- return FALSE; +- } ++ gam_inotify_send_initial_events (gam_subscription_get_path (sub), sub, gam_subscription_is_dir (sub), FALSE); + +- lstat (path, &sbuf); +- data->dir = (sbuf.st_mode & S_IFDIR); +- data->wd = wd; +- g_hash_table_insert(wd_hash, GINT_TO_POINTER(data->wd), data); +- gam_inotify_send_initial_events_all (data); +- data->missing = FALSE; +- data->permission = FALSE; + return TRUE; + } + +-static gint links_list_compare (gconstpointer a, gconstpointer b) +-{ +- const inotify_links_t *links = NULL; +- +- g_assert (a); +- g_assert (b); +- links = a; +- g_assert (links->path); +- +- return strcmp (links->path, b); +-} +- +-static void +-gam_inotify_add_link (const char *path) +-{ +- inotify_links_t *links = NULL; +- struct stat sbuf; +- +- g_assert (path); +- +- links = g_new0 (inotify_links_t, 1); +- +- g_assert (links); +- +- GAM_DEBUG (DEBUG_INFO, "inotify-link: add - %s\n", path); +- links->path = g_strdup (path); +- links->scan_interval = gam_fs_get_poll_timeout (path); +- links->last_scan_time = 0; +- lstat(path, &sbuf); +- links->sbuf = sbuf; +- links_list = g_list_prepend (links_list, links); +-} +- +-static void +-gam_inotify_rm_link (const char *path) ++static gboolean ++gam_inotify_remove_sub_pred (ih_sub_t *sub, void *callerdata) + { +- GList *node = NULL; +- inotify_links_t *links = NULL; +- +- g_assert (path && *path); +- +- node = g_list_find_custom (links_list, path, links_list_compare); +- +- if (!node) +- return; +- +- GAM_DEBUG (DEBUG_INFO, "inotify-link: rm - %s\n", path); +- links = node->data; +- g_free (links->path); +- g_free (links); +- +- links_list = g_list_remove_link (links_list, node); +- ++ return sub->usersubdata == callerdata; + } + +-static gboolean +-gam_inotify_scan_links (gpointer userdata) ++gboolean ++gam_inotify_remove_subscription (GamSubscription *sub) + { +- guint i; +- +- gam_inotify_sanity_check (); +- /* We have to walk the list like this because entries might be removed while we walk the list */ +- for (i = 0; ; i++) +- { +- inotify_links_t *links = g_list_nth_data (links_list, i); +- +- if (!links) +- break; +- +- /* Not enough time has passed since the last scan */ +- if (time(NULL) - links->last_scan_time < links->scan_interval) +- continue; +- +- links->last_scan_time = time(NULL); +- if (!gam_inotify_is_link (links->path)) +- { +- if (gam_inotify_nolonger_link (links->path)) +- { +- gam_inotify_rm_link (links->path); +- } +- } else { +- gam_inotify_poll_link (links); +- } +- +- } ++ ih_sub_foreach_free (sub, gam_inotify_remove_sub_pred); + +- gam_inotify_sanity_check (); + return TRUE; + } + + static gboolean +-gam_inotify_stat_changed (struct stat sbuf1, struct stat sbuf2) +-{ +-#ifdef ST_MTIM_NSEC +- return ((sbuf1.st_mtim.tv_sec != sbuf2.st_mtim.tv_sec) || +- (sbuf1.st_mtim.tv_nsec != sbuf2.st_mtim.tv_nsec) || +- (sbuf1.st_size != sbuf2.st_size) || +- (sbuf1.st_ctim.tv_sec != sbuf2.st_ctim.tv_sec) || +- (sbuf1.st_ctim.tv_nsec != sbuf2.st_ctim.tv_nsec)); +-#else +- return ((sbuf1.st_mtime != sbuf2.st_mtime) || +- (sbuf1.st_size != sbuf2.st_size) || +- (sbuf1.st_ctime != sbuf2.st_ctime)); +-#endif +-} +- +-static void +-gam_inotify_poll_link (inotify_links_t *links) +-{ +- struct stat sbuf; +- g_assert (links); +- +- /* Next time around, we will detect the deletion, and send the event */ +- if (lstat (links->path, &sbuf) < 0) +- return; +- +- if (gam_inotify_stat_changed (sbuf, links->sbuf)) +- { +- inotify_data_t *data = g_hash_table_lookup (path_hash, links->path); +- g_assert (data); +- gam_server_emit_event (data->path, data->dir, GAMIN_EVENT_CHANGED, data->subs, 1); +- } +- +- links->sbuf = sbuf; +-} +- +-static void +-gam_inotify_wd_check (gpointer key, gpointer value, gpointer user_data) +-{ +- gint wd = GPOINTER_TO_INT(key); +- inotify_data_t *data = (inotify_data_t *)value; +- if (wd < 0) +- { +- GAM_DEBUG (DEBUG_INFO, "inotify-sanity: FAILURE wd hash for %s key < 0\n", data->path); +- } +- if (data->wd < 0) +- { +- GAM_DEBUG (DEBUG_INFO, "inotify-sanity: FAILURE wd hash for %s value < 0\n", data->path); +- } +- if (data->wd != wd) +- { +- GAM_DEBUG (DEBUG_INFO, "inotify-sanity: FAILURE wd hash value & key don't match\n"); +- } +-} +- +-static void +-gam_inotify_wd_hash_sanity_check (void) +-{ +- g_hash_table_foreach (wd_hash, gam_inotify_wd_check, NULL); +-} +- +-static void +-gam_inotify_missing_check (gpointer data, gpointer user_data) ++gam_inotify_remove_listener_pred (ih_sub_t *sub, void *callerdata) + { +- inotify_missing_t *missing = data; +- inotify_data_t *idata = NULL; +- +- if (!missing) +- { +- GAM_DEBUG (DEBUG_INFO, "inotify-sanity: Missing check called with NULL argument\n"); +- return; +- } +- +- if (!missing->path) +- { +- GAM_DEBUG (DEBUG_INFO, "inotify-sanity: Missing entry missing path name\n"); +- return; +- } +- +- idata = g_hash_table_lookup (path_hash, missing->path); +- +- if (!idata) +- { +- GAM_DEBUG (DEBUG_INFO, "inotify-sanity: Could not find %s in path hash table\n", missing->path); +- return; +- } +- +- if (idata->wd != GAM_INOTIFY_WD_MISSING && idata->wd != GAM_INOTIFY_WD_PERM) +- { +- GAM_DEBUG (DEBUG_INFO, "inotify-sanity: data->wd != GAM_INOTIFY_WD_(MISSING/PERM) for path in missing list\n"); +- return; +- } +- +- if (idata->missing != TRUE && idata->permission != TRUE) +- { +- GAM_DEBUG (DEBUG_INFO, "inotify-sanity: data->missing/permission != TRUE for path in missing list\n"); +- return; +- } +- +- if (idata->missing == TRUE && idata->wd != GAM_INOTIFY_WD_MISSING) +- { +- GAM_DEBUG (DEBUG_INFO, "inotify-sanity: data->missing == TRUE && idata->wd != GAM_INOTIFY_WD_MISSING\n"); +- return; +- } +- +- if (idata->permission == TRUE && idata->wd != GAM_INOTIFY_WD_PERM) +- { +- GAM_DEBUG (DEBUG_INFO, "inotify-sanity: data->permission == TRUE && idata->wd != GAM_INOTIFY_WD_PERM\n"); +- return; +- } ++ GamSubscription *gsub = (GamSubscription *)sub->usersubdata; + +- if (idata->wd == GAM_INOTIFY_WD_MISSING && idata->missing != TRUE) +- { +- GAM_DEBUG (DEBUG_INFO, "inotify-sanity: data->missing == FALSE && idata->wd == GAM_INOTIFY_WD_MISSING\n"); +- return; +- } +- +- if (idata->wd == GAM_INOTIFY_WD_PERM && idata->permission != TRUE) +- { +- GAM_DEBUG (DEBUG_INFO, "inotify-sanity: data->permission != TRUE && idata->wd == GAM_INOTIFY_WD_PERM\n"); +- return; +- } ++ return gam_subscription_get_listener (gsub) == callerdata; + } + +-static void +-gam_inotify_missing_list_sanity_check (void) ++gboolean ++gam_inotify_remove_all_for (GamListener *listener) + { +- g_list_foreach (missing_list, gam_inotify_missing_check, NULL); +-} +- ++ ih_sub_foreach_free (listener, gam_inotify_remove_listener_pred); + +-static void +-gam_inotify_sanity_check (void) +-{ +-#ifdef GAM_INOTIFY_SANITY +- gam_inotify_wd_hash_sanity_check (); +- gam_inotify_missing_list_sanity_check (); +-#endif ++ return TRUE; + } + +-static gboolean +-g_timeval_lt(GTimeVal *val1, GTimeVal *val2) ++void ++gam_inotify_debug (void) + { +- 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; ++ id_dump (NULL); + } + +-static gboolean +-g_timeval_eq(GTimeVal *val1, GTimeVal *val2) ++gboolean ++gam_inotify_is_running (void) + { +- return (val1->tv_sec == val2->tv_sec) && (val1->tv_usec == val2->tv_usec); ++ return ih_running (); + } +- +--- /dev/null 2006-08-28 15:22:40.902752500 +0200 ++++ gamin-0.1.7/server/inotify-sub.c 2006-09-05 11:01:21.000000000 +0200 +@@ -0,0 +1,121 @@ ++/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ ++ ++/* inotify-helper.c - Gnome VFS Monitor based on inotify. ++ ++ Copyright (C) 2006 John McCutchan ++ ++ The Gnome Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Library General Public License as ++ published by the Free Software Foundation; either version 2 of the ++ License, or (at your option) any later version. ++ ++ The Gnome 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 ++ Library General Public License for more details. ++ ++ You should have received a copy of the GNU Library General Public ++ License along with the Gnome Library; see the file COPYING.LIB. If not, ++ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, ++ Boston, MA 02111-1307, USA. ++ ++ Authors: ++ John McCutchan ++*/ ++ ++#include "config.h" ++#include ++#include ++#include "gam_subscription.h" ++#include "inotify-sub.h" ++ ++static gboolean is_debug_enabled = FALSE; ++#define IS_W if (is_debug_enabled) g_warning ++ ++static void ih_sub_setup (ih_sub_t *sub); ++ ++ih_sub_t * ++ih_sub_new (const char *pathname, gboolean is_dir, guint32 flags, void *userdata) ++{ ++ ih_sub_t *sub = NULL; ++ ++ sub = g_new0 (ih_sub_t, 1); ++ sub->usersubdata = userdata; ++ sub->is_dir = is_dir; ++ sub->extra_flags = flags; ++ sub->pathname = g_strdup (pathname); ++ ++ IS_W("new subscription for %s being setup\n", sub->pathname); ++ ++ ih_sub_setup (sub); ++ return sub; ++} ++ ++void ++ih_sub_free (ih_sub_t *sub) ++{ ++ if (sub->filename) ++ g_free (sub->filename); ++ if (sub->dirname) ++ g_free (sub->dirname); ++ g_free (sub->pathname); ++ g_free (sub); ++} ++ ++static ++gchar *ih_sub_get_dirname (gchar *pathname) ++{ ++ return g_path_get_dirname (pathname); ++} ++ ++static ++gchar *ih_sub_get_filename (gchar *pathname) ++{ ++ gchar *out; ++ // FIXME: return filename here ++ return out; ++} ++ ++static ++void ih_sub_fix_dirname (ih_sub_t *sub) ++{ ++ size_t len = 0; ++ ++ g_assert (sub->dirname); ++ ++ len = strlen (sub->dirname); ++ ++ /* We need to strip a trailing slash ++ * to get the correct behaviour ++ * out of the kernel ++ */ ++ if (sub->dirname[len] == '/') ++ sub->dirname[len] = '\0'; ++} ++ ++/* ++ * XXX: Currently we just follow the gnome vfs monitor type flags when ++ * deciding how to treat the path. In the future we could try ++ * and determine whether the path points to a directory or a file but ++ * that is racey. ++ */ ++static void ++ih_sub_setup (ih_sub_t *sub) ++{ ++ if (sub->is_dir) ++ { ++ sub->dirname = g_strdup (sub->pathname); ++ sub->filename = NULL; ++ } else { ++ sub->dirname = ih_sub_get_dirname (sub->pathname); ++ sub->filename = ih_sub_get_filename (sub->pathname); ++ } ++ ++ ih_sub_fix_dirname (sub); ++ ++ IS_W("sub->dirname = %s\n", sub->dirname); ++ if (sub->filename) ++ { ++ IS_W("sub->filename = %s\n", sub->filename); ++ } ++} +--- /dev/null 2006-08-28 15:22:40.902752500 +0200 ++++ gamin-0.1.7/server/inotify-helper.c 2006-09-05 11:01:21.000000000 +0200 +@@ -0,0 +1,234 @@ ++/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ ++ ++/* inotify-helper.c - Gnome VFS Monitor based on inotify. ++ ++ Copyright (C) 2005 John McCutchan ++ ++ The Gnome Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Library General Public License as ++ published by the Free Software Foundation; either version 2 of the ++ License, or (at your option) any later version. ++ ++ The Gnome 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 ++ Library General Public License for more details. ++ ++ You should have received a copy of the GNU Library General Public ++ License along with the Gnome Library; see the file COPYING.LIB. If not, ++ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, ++ Boston, MA 02111-1307, USA. ++ ++ Authors: ++ John McCutchan ++*/ ++ ++#include "config.h" ++#include ++#include ++#include ++#include ++/* Just include the local header to stop all the pain */ ++#include "local_inotify.h" ++#if 0 ++#ifdef HAVE_SYS_INOTIFY_H ++/* We don't actually include the libc header, because there has been ++ * problems with libc versions that was built without inotify support. ++ * Instead we use the local version. ++ */ ++#include "local_inotify.h" ++#elif defined (HAVE_LINUX_INOTIFY_H) ++#include ++#endif ++#endif ++#include "inotify-helper.h" ++#include "inotify-missing.h" ++#include "inotify-path.h" ++#include "inotify-diag.h" ++ ++static gboolean ih_debug_enabled = FALSE; ++#define IH_W if (ih_debug_enabled) g_warning ++ ++static void ih_event_callback (ik_event_t *event, ih_sub_t *sub); ++static void ih_found_callback (ih_sub_t *sub); ++ ++/* We share this lock with inotify-kernel.c and inotify-missing.c ++ * ++ * inotify-kernel.c takes the lock when it reads events from ++ * the kernel and when it processes those events ++ * ++ * inotify-missing.c takes the lock when it is scanning the missing ++ * list. ++ * ++ * We take the lock in all public functions ++ */ ++G_LOCK_DEFINE (inotify_lock); ++static GList *sub_list = NULL; ++static gboolean initialized = FALSE; ++static event_callback_t user_ecb = NULL; ++static found_callback_t user_fcb = NULL; ++ ++/** ++ * Initializes the inotify backend. This must be called before ++ * any other functions in this module. ++ * ++ * @returns TRUE if initialization succeeded, FALSE otherwise ++ */ ++gboolean ++ih_startup (event_callback_t ecb, ++ found_callback_t fcb) ++{ ++ static gboolean result = FALSE; ++ ++ G_LOCK(inotify_lock); ++ ++ if (initialized == TRUE) { ++ G_UNLOCK(inotify_lock); ++ return result; ++ } ++ ++ initialized = TRUE; ++ ++ result = ip_startup (ih_event_callback); ++ if (!result) { ++ g_warning( "Could not initialize inotify\n"); ++ G_UNLOCK(inotify_lock); ++ return FALSE; ++ } ++ user_ecb = ecb; ++ user_fcb = fcb; ++ im_startup (ih_found_callback); ++ id_startup (); ++ ++ IH_W ("started gnome-vfs inotify backend\n"); ++ ++ G_UNLOCK(inotify_lock); ++ return TRUE; ++} ++ ++gboolean ++ih_running (void) ++{ ++ return initialized; ++} ++ ++/** ++ * Adds a subscription to be monitored. ++ */ ++gboolean ++ih_sub_add (ih_sub_t * sub) ++{ ++ G_LOCK(inotify_lock); ++ ++ g_assert (g_list_find (sub_list, sub) == NULL); ++ ++ // make sure that sub isn't on sub_list first. ++ if (!ip_start_watching (sub)) ++ { ++ im_add (sub); ++ } ++ ++ sub_list = g_list_prepend (sub_list, sub); ++ ++ G_UNLOCK(inotify_lock); ++ return TRUE; ++} ++ ++/** ++ * Cancels a subscription which was being monitored. ++ */ ++gboolean ++ih_sub_cancel (ih_sub_t * sub) ++{ ++ G_LOCK(inotify_lock); ++ ++ ++ if (!sub->cancelled) ++ { ++ IH_W("cancelling %s\n", sub->pathname); ++ g_assert (g_list_find (sub_list, sub) != NULL); ++ sub->cancelled = TRUE; ++ im_rm (sub); ++ ip_stop_watching (sub); ++ sub_list = g_list_remove (sub_list, sub); ++ } ++ ++ G_UNLOCK(inotify_lock); ++ return TRUE; ++} ++ ++static void ++ih_sub_foreach_worker (void *callerdata, gboolean (*f)(ih_sub_t *sub, void *callerdata), gboolean free) ++{ ++ GList *l = NULL; ++ GList *removed = NULL; ++ ++ G_LOCK(inotify_lock); ++ ++ for (l = sub_list; l; l = l->next) ++ { ++ ih_sub_t *sub = l->data; ++ ++ if (f(sub, callerdata)) ++ { ++ removed = g_list_prepend (removed, l); ++ ih_sub_cancel (sub); ++ if (free) ++ ih_sub_free (sub); ++ } ++ } ++ ++ for (l = removed; l ; l = l->next) ++ { ++ GList *llink = l->data; ++ sub_list = g_list_remove_link (sub_list, llink); ++ g_list_free_1 (llink); ++ } ++ ++ G_UNLOCK(inotify_lock); ++} ++ ++void ++ih_sub_foreach (void *callerdata, gboolean (*f)(ih_sub_t *sub, void *callerdata)) ++{ ++ ih_sub_foreach_worker (callerdata, f, FALSE); ++} ++ ++void ++ih_sub_foreach_free (void *callerdata, gboolean (*f)(ih_sub_t *sub, void *callerdata)) ++{ ++ ih_sub_foreach_worker (callerdata, f, TRUE); ++} ++ ++static void ih_event_callback (ik_event_t *event, ih_sub_t *sub) ++{ ++ gchar *fullpath; ++ if (event->name) ++ { ++ fullpath = g_strdup_printf ("%s/%s", sub->dirname, event->name); ++ } else { ++ fullpath = g_strdup_printf ("%s/", sub->dirname); ++ } ++ ++ user_ecb (fullpath, event->mask, sub->usersubdata); ++ g_free(fullpath); ++} ++ ++static void ih_found_callback (ih_sub_t *sub) ++{ ++ gchar *fullpath; ++ ++ if (sub->filename) ++ { ++ fullpath = g_strdup_printf ("%s/%s", sub->dirname, sub->filename); ++ if (!g_file_test (fullpath, G_FILE_TEST_EXISTS)) { ++ g_free (fullpath); ++ return; ++ } ++ } else { ++ fullpath = g_strdup_printf ("%s/", sub->dirname); ++ } ++ ++ user_fcb (fullpath, sub->usersubdata); ++ g_free(fullpath); ++} +--- /dev/null 2006-08-28 15:22:40.902752500 +0200 ++++ gamin-0.1.7/server/inotify-diag.c 2006-09-05 11:01:21.000000000 +0200 +@@ -0,0 +1,67 @@ ++/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ ++ ++/* inotify-helper.c - Gnome VFS Monitor based on inotify. ++ ++ Copyright (C) 2005 John McCutchan ++ ++ The Gnome Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Library General Public License as ++ published by the Free Software Foundation; either version 2 of the ++ License, or (at your option) any later version. ++ ++ The Gnome 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 ++ Library General Public License for more details. ++ ++ You should have received a copy of the GNU Library General Public ++ License along with the Gnome Library; see the file COPYING.LIB. If not, ++ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, ++ Boston, MA 02111-1307, USA. ++ ++ Authors: ++ John McCutchan ++*/ ++ ++#include "config.h" ++#include ++#include ++#include ++#include "inotify-missing.h" ++#include "inotify-path.h" ++#include "inotify-diag.h" ++ ++#define DIAG_DUMP_TIME 20000 /* 20 seconds */ ++G_LOCK_EXTERN (inotify_lock); ++ ++gboolean id_dump (gpointer userdata) ++{ ++ G_LOCK (inotify_lock); ++ GIOChannel *ioc = NULL; ++ pid_t pid = getpid(); ++ char *fname = g_strdup_printf("/tmp/gvfsid.%d", pid); ++ ioc = g_io_channel_new_file (fname, "w", NULL); ++ g_free (fname); ++ if (!ioc) ++ { ++ G_UNLOCK (inotify_lock); ++ return TRUE; ++ } ++ ++ im_diag_dump (ioc); ++ ++ g_io_channel_shutdown (ioc, TRUE, NULL); ++ g_io_channel_unref (ioc); ++ G_UNLOCK (inotify_lock); ++ return TRUE; ++} ++ ++void id_startup () ++{ ++ if (!g_getenv ("GNOME_VFS_INOTIFY_DIAG")) ++ { ++ return; ++ } ++ ++ g_timeout_add (DIAG_DUMP_TIME, id_dump, NULL); ++} +--- /dev/null 2006-08-28 15:22:40.902752500 +0200 ++++ gamin-0.1.7/server/inotify-path.h 2006-09-05 11:01:21.000000000 +0200 +@@ -0,0 +1,27 @@ ++/* ++ Copyright (C) 2006 John McCutchan ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; version 2. ++ ++ This program 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 General Public License version 2 for more details. ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software Foundation, ++ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++*/ ++ ++#ifndef __INOTIFY_PATH_H ++#define __INOTIFY_PATH_H ++ ++#include "inotify-kernel.h" ++#include "inotify-sub.h" ++ ++gboolean ip_startup (void (*event_cb)(ik_event_t *event, ih_sub_t *sub)); ++gboolean ip_start_watching (ih_sub_t *sub); ++gboolean ip_stop_watching (ih_sub_t *sub); ++ ++#endif +--- /dev/null 2006-08-28 15:22:40.902752500 +0200 ++++ gamin-0.1.7/server/inotify-path.c 2006-09-05 11:01:21.000000000 +0200 +@@ -0,0 +1,387 @@ ++/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ ++ ++/* inotify-helper.c - Gnome VFS Monitor based on inotify. ++ ++ Copyright (C) 2006 John McCutchan ++ ++ The Gnome Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Library General Public License as ++ published by the Free Software Foundation; either version 2 of the ++ License, or (at your option) any later version. ++ ++ The Gnome 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 ++ Library General Public License for more details. ++ ++ You should have received a copy of the GNU Library General Public ++ License along with the Gnome Library; see the file COPYING.LIB. If not, ++ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, ++ Boston, MA 02111-1307, USA. ++ ++ Authors: ++ John McCutchan ++*/ ++ ++#include "config.h" ++ ++/* Don't put conflicting kernel types in the global namespace: */ ++#define __KERNEL_STRICT_NAMES ++ ++#include "local_inotify.h" ++#if 0 ++#ifdef HAVE_SYS_INOTIFY_H ++/* We don't actually include the libc header, because there has been ++ * problems with libc versions that was built without inotify support. ++ * Instead we use the local version. ++ */ ++#include "local_inotify.h" ++#elif defined (HAVE_LINUX_INOTIFY_H) ++#include ++#endif ++#endif ++#include ++#include ++#include "inotify-kernel.h" ++#include "inotify-path.h" ++#include "inotify-missing.h" ++ ++#define IP_INOTIFY_MASK (IN_MODIFY|IN_ATTRIB|IN_MOVED_FROM|IN_MOVED_TO|IN_DELETE|IN_CREATE|IN_DELETE_SELF|IN_UNMOUNT|IN_MOVE_SELF) ++ ++typedef struct ip_watched_dir_s { ++ char *path; ++ /* TODO: We need to maintain a tree of watched directories ++ * so that we can deliver move/delete events to sub folders. ++ * Or the application could do it... ++ */ ++ struct ip_watched_dir_s *parent; ++ GList * children; ++ ++ /* Inotify state */ ++ gint32 wd; ++ ++ /* List of inotify subscriptions */ ++ GList *subs; ++} ip_watched_dir_t; ++ ++static gboolean ip_debug_enabled = FALSE; ++#define IP_W if (ip_debug_enabled) g_warning ++ ++/* path -> ip_watched_dir */ ++static GHashTable * path_dir_hash = NULL; ++/* ih_sub_t * -> ip_watched_dir * ++ * ++ * Each subscription is attached to a watched directory or it is on ++ * the missing list ++ */ ++static GHashTable * sub_dir_hash = NULL; ++/* This hash holds GLists of ip_watched_dir_t *'s ++ * We need to hold a list because symbolic links can share ++ * the same wd ++ */ ++static GHashTable * wd_dir_hash = NULL; ++ ++static ip_watched_dir_t * ip_watched_dir_new (const char *path, int wd); ++static void ip_watched_dir_free (ip_watched_dir_t *dir); ++static void ip_event_callback (ik_event_t *event); ++ ++static void (*event_callback)(ik_event_t *event, ih_sub_t *sub); ++ ++gboolean ip_startup (void (*cb)(ik_event_t *event, ih_sub_t *sub)) ++{ ++ static gboolean initialized = FALSE; ++ static gboolean result = FALSE; ++ ++ if (initialized == TRUE) { ++ return result; ++ } ++ ++ initialized = TRUE; ++ event_callback = cb; ++ result = ik_startup (ip_event_callback); ++ ++ if (!result) { ++ return FALSE; ++ } ++ ++ path_dir_hash = g_hash_table_new(g_str_hash, g_str_equal); ++ sub_dir_hash = g_hash_table_new(g_direct_hash, g_direct_equal); ++ wd_dir_hash = g_hash_table_new(g_direct_hash, g_direct_equal); ++ ++ return TRUE; ++} ++ ++static void ++ip_map_path_dir (const char *path, ip_watched_dir_t *dir) ++{ ++ g_assert (path && dir); ++ g_hash_table_insert(path_dir_hash, dir->path, dir); ++} ++ ++static void ++ip_map_sub_dir (ih_sub_t *sub, ip_watched_dir_t *dir) ++{ ++ /* Associate subscription and directory */ ++ g_assert (dir && sub); ++ g_hash_table_insert (sub_dir_hash, sub, dir); ++ dir->subs = g_list_prepend (dir->subs, sub); ++} ++ ++static void ++ip_map_wd_dir (gint32 wd, ip_watched_dir_t *dir) ++{ ++ g_assert (wd >= 0 && dir); ++ GList *dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER(wd)); ++ dir_list = g_list_prepend (dir_list, dir); ++ g_hash_table_replace(wd_dir_hash, GINT_TO_POINTER(dir->wd), dir_list); ++} ++ ++gboolean ip_start_watching (ih_sub_t *sub) ++{ ++ gint32 wd; ++ int err; ++ ip_watched_dir_t *dir; ++ ++ g_assert (sub); ++ g_assert (!sub->cancelled); ++ g_assert (sub->dirname); ++ ++ IP_W("Starting to watch %s\n", sub->dirname); ++ dir = g_hash_table_lookup (path_dir_hash, sub->dirname); ++ if (dir) ++ { ++ IP_W("Already watching\n"); ++ goto out; ++ } ++ ++ IP_W("Trying to add inotify watch "); ++ wd = ik_watch (sub->dirname, IP_INOTIFY_MASK|IN_ONLYDIR|sub->extra_flags, &err); ++ if (wd < 0) ++ { ++ IP_W("Failed\n"); ++ return FALSE; ++ } else { ++ /* Create new watched directory and associate it with the ++ * wd hash and path hash ++ */ ++ IP_W("Success\n"); ++ dir = ip_watched_dir_new (sub->dirname, wd); ++ ip_map_wd_dir (wd, dir); ++ ip_map_path_dir (sub->dirname, dir); ++ } ++ ++out: ++ ip_map_sub_dir (sub, dir); ++ ++ return TRUE; ++} ++ ++static void ++ip_unmap_path_dir (const char *path, ip_watched_dir_t *dir) ++{ ++ g_assert (path && dir); ++ g_hash_table_remove (path_dir_hash, dir->path); ++} ++ ++static void ++ip_unmap_wd_dir (gint32 wd, ip_watched_dir_t *dir) ++{ ++ GList *dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER(wd)); ++ if (!dir_list) ++ return; ++ g_assert (wd >= 0 && dir); ++ dir_list = g_list_remove (dir_list, dir); ++ if (dir_list == NULL) { ++ g_hash_table_remove (wd_dir_hash, GINT_TO_POINTER(dir->wd)); ++ } else { ++ g_hash_table_replace (wd_dir_hash, GINT_TO_POINTER(dir->wd), dir_list); ++ } ++} ++ ++static void ++ip_unmap_wd (gint32 wd) ++{ ++ GList *dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER(wd)); ++ if (!dir_list) ++ return; ++ g_assert (wd >= 0); ++ g_hash_table_remove (wd_dir_hash, GINT_TO_POINTER(wd)); ++ g_list_free (dir_list); ++} ++ ++static void ++ip_unmap_sub_dir (ih_sub_t *sub, ip_watched_dir_t *dir) ++{ ++ g_assert (sub && dir); ++ g_hash_table_remove (sub_dir_hash, sub); ++ dir->subs = g_list_remove (dir->subs, sub); ++} ++ ++static void ++ip_unmap_all_subs (ip_watched_dir_t *dir) ++{ ++ GList *l = NULL; ++ ++ for (l = dir->subs; l; l = l->next) ++ { ++ ih_sub_t *sub = l->data; ++ g_hash_table_remove (sub_dir_hash, sub); ++ } ++ g_list_free (dir->subs); ++ dir->subs = NULL; ++} ++ ++gboolean ip_stop_watching (ih_sub_t *sub) ++{ ++ ip_watched_dir_t *dir = NULL; ++ ++ dir = g_hash_table_lookup (sub_dir_hash, sub); ++ if (!dir) { ++ return TRUE; ++ } ++ ++ ip_unmap_sub_dir (sub, dir); ++ ++ /* No one is subscribing to this directory any more */ ++ if (dir->subs == NULL) { ++ ik_ignore (dir->path, dir->wd); ++ ip_unmap_wd_dir (dir->wd, dir); ++ ip_unmap_path_dir (dir->path, dir); ++ ip_watched_dir_free (dir); ++ } ++ ++ return TRUE; ++} ++ ++ ++static ip_watched_dir_t * ++ip_watched_dir_new (const char *path, gint32 wd) ++{ ++ ip_watched_dir_t *dir = g_new0(ip_watched_dir_t, 1); ++ ++ dir->path = g_strdup(path); ++ dir->wd = wd; ++ ++ return dir; ++} ++ ++static void ++ip_watched_dir_free (ip_watched_dir_t * dir) ++{ ++ g_assert (dir->subs == 0); ++ g_free(dir->path); ++ g_free(dir); ++} ++ ++static void ip_wd_delete (gpointer data, gpointer user_data) ++{ ++ ip_watched_dir_t *dir = data; ++ GList *l = NULL; ++ ++ for (l = dir->subs; l; l = l->next) ++ { ++ ih_sub_t *sub = l->data; ++ ++ /* Add subscription to missing list */ ++ im_add (sub); ++ } ++ ip_unmap_all_subs (dir); ++ /* Unassociate the path and the directory */ ++ ip_unmap_path_dir (dir->path, dir); ++ ip_watched_dir_free (dir); ++} ++ ++static void ip_event_dispatch (GList *dir_list, GList *pair_dir_list, ik_event_t *event) ++{ ++ GList *dirl; ++ ++ if (!event) ++ return; ++ ++ /* TODO: ++ * ++ * Figure out how we will deliver move events ++ */ ++ for (dirl = dir_list; dirl; dirl = dirl->next) ++ { ++ GList *subl; ++ ip_watched_dir_t *dir = dirl->data; ++ ++ for (subl = dir->subs; subl; subl = subl->next) ++ { ++ ih_sub_t *sub = subl->data; ++ ++ /* If the event and the subscription have a filename ++ * they need to match before the event could be delivered. ++ */ ++ if (event->name && sub->filename) { ++ if (strcmp (event->name, sub->filename)) ++ continue; ++ /* If the event doesn't have a filename, but the subscription does ++ * we shouldn't deliever the event */ ++ } else if (sub->filename) ++ continue; ++ ++ event_callback (event, sub); ++ } ++ } ++ ++ if (!event->pair) ++ return; ++ ++ for (dirl = pair_dir_list; dirl; dirl = dirl->next) ++ { ++ GList *subl; ++ ip_watched_dir_t *dir = dirl->data; ++ ++ for (subl = dir->subs; subl; subl = subl->next) ++ { ++ ih_sub_t *sub = subl->data; ++ ++ /* If the event and the subscription have a filename ++ * they need to match before the event could be delivered. ++ */ ++ if (event->pair->name && sub->filename) { ++ if (strcmp (event->pair->name, sub->filename)) ++ continue; ++ /* If the event doesn't have a filename, but the subscription does ++ * we shouldn't deliever the event */ ++ } else if (sub->filename) ++ continue; ++ ++ event_callback (event->pair, sub); ++ } ++ } ++} ++ ++static void ++ip_event_callback (ik_event_t *event) ++{ ++ GList *dir_list = NULL; ++ GList *pair_dir_list = NULL; ++ ++ dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER(event->wd)); ++ ++ /* We can ignore IN_IGNORED events */ ++ if (event->mask & IN_IGNORED) { ++ ik_event_free (event); ++ return; ++ } ++ ++ if (event->pair) ++ pair_dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER(event->pair->wd)); ++ ++ if (event->mask & IP_INOTIFY_MASK) ++ ip_event_dispatch (dir_list, pair_dir_list, event); ++ ++ /* We have to manage the missing list when we get a DELETE event. */ ++ if (event->mask & IN_DELETE_SELF || event->mask & IN_MOVE_SELF) ++ { ++ /* Add all subscriptions to missing list */ ++ g_list_foreach (dir_list, ip_wd_delete, NULL); ++ /* Unmap all directories attached to this wd */ ++ ip_unmap_wd (event->wd); ++ } ++ ++ ik_event_free (event); ++} +--- /dev/null 2006-08-28 15:22:40.902752500 +0200 ++++ gamin-0.1.7/server/inotify-helper.h 2006-09-05 11:01:21.000000000 +0200 +@@ -0,0 +1,45 @@ ++/* inotify-helper.h - GNOME VFS Monitor using inotify ++ ++ Copyright (C) 2005 John McCutchan ++ ++ The Gnome Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Library General Public License as ++ published by the Free Software Foundation; either version 2 of the ++ License, or (at your option) any later version. ++ ++ The Gnome 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 ++ Library General Public License for more details. ++ ++ You should have received a copy of the GNU Library General Public ++ License along with the Gnome Library; see the file COPYING.LIB. If not, ++ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, ++ Boston, MA 02111-1307, USA. ++ ++ Author: John McCutchan ++*/ ++ ++ ++#ifndef __INOTIFY_HELPER_H ++#define __INOTIFY_HELPER_H ++ ++#include "inotify-sub.h" ++#include "inotify-kernel.h" ++ ++typedef void (*event_callback_t)(const char *fullpath, guint32 mask, void *subdata); ++typedef void (*found_callback_t)(const char *fullpath, void *subdata); ++ ++gboolean ih_startup (event_callback_t ecb, ++ found_callback_t fcb); ++gboolean ih_running (void); ++gboolean ih_sub_add (ih_sub_t *sub); ++gboolean ih_sub_cancel (ih_sub_t *sub); ++ ++/* Return FALSE from 'f' if the subscription should be cancelled */ ++void ih_sub_foreach (void *callerdata, gboolean (*f)(ih_sub_t *sub, void *callerdata)); ++ ++/* Return FALSE from 'f' if the subscription should be cancelled and free'd */ ++void ih_sub_foreach_free (void *callerdata, gboolean (*f)(ih_sub_t *sub, void *callerdata)); ++ ++#endif /* __INOTIFY_HELPER_H */ +--- /dev/null 2006-08-28 15:22:40.902752500 +0200 ++++ gamin-0.1.7/server/inotify-sub.h 2006-09-05 11:01:21.000000000 +0200 +@@ -0,0 +1,42 @@ ++/* inotify-helper.h - GNOME VFS Monitor using inotify ++ ++ Copyright (C) 2006 John McCutchan ++ ++ The Gnome Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Library General Public License as ++ published by the Free Software Foundation; either version 2 of the ++ License, or (at your option) any later version. ++ ++ The Gnome 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 ++ Library General Public License for more details. ++ ++ You should have received a copy of the GNU Library General Public ++ License along with the Gnome Library; see the file COPYING.LIB. If not, ++ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, ++ Boston, MA 02111-1307, USA. ++ ++ Author: John McCutchan ++*/ ++ ++ ++#ifndef __INOTIFY_SUB_H ++#define __INOTIFY_SUB_H ++ ++#include "gam_subscription.h" ++ ++typedef struct { ++ gboolean is_dir; ++ char *pathname; ++ char *dirname; ++ char *filename; ++ guint32 extra_flags; ++ gboolean cancelled; ++ void *usersubdata; ++} ih_sub_t; ++ ++ih_sub_t *ih_sub_new (const char *pathname, gboolean is_dir, guint32 flags, void *userdata); ++void ih_sub_free (ih_sub_t *sub); ++ ++#endif /* __INOTIFY_SUB_H */ +--- gamin-0.1.7/server/local_inotify.h.new-inotify-backend 2005-08-17 15:50:04.000000000 +0200 ++++ gamin-0.1.7/server/local_inotify.h 2006-09-05 11:01:21.000000000 +0200 +@@ -47,6 +47,9 @@ + #define IN_MOVE (IN_MOVED_FROM | IN_MOVED_TO) /* moves */ + + /* special flags */ ++#define IN_ONLYDIR 0x01000000 /* only watch the path if it is a directory */ ++#define IN_DONT_FOLLOW 0x02000000 /* don't follow a sym link */ ++#define IN_MASK_ADD 0x20000000 /* add to the mask of an already existing watch */ + #define IN_ISDIR 0x40000000 /* event occurred against dir */ + #define IN_ONESHOT 0x80000000 /* only send event once */ + diff --git a/app-admin/gamin/files/gamin-no-timers.patch b/app-admin/gamin/files/gamin-no-timers.patch new file mode 100644 index 0000000..b2e2135 --- /dev/null +++ b/app-admin/gamin/files/gamin-no-timers.patch @@ -0,0 +1,284 @@ +Index: lib/gam_error.c +=================================================================== +RCS file: /cvs/gnome/gamin/lib/gam_error.c,v +retrieving revision 1.11 +diff -u -p -r1.11 gam_error.c +--- lib/gam_error.c 19 Apr 2006 08:28:14 -0000 1.11 ++++ lib/gam_error.c 5 Sep 2006 14:02:43 -0000 +@@ -18,6 +18,7 @@ + typedef void (*signal_handler) (int); + + extern void gam_show_debug(void); ++extern void gam_got_signal (void); + + int gam_debug_active = 0; + static int initialized = 0; +@@ -60,11 +61,13 @@ gam_error_handle_signal(void) + } + } + ++ + static void + gam_error_signal(int no) + { + got_signal = !got_signal; + gam_debug_active = -1; /* force going into gam_debug() */ ++ gam_got_signal (); + } + + /** +Index: libgamin/gam_api.c +=================================================================== +RCS file: /cvs/gnome/gamin/libgamin/gam_api.c,v +retrieving revision 1.40 +diff -u -p -r1.40 gam_api.c +--- libgamin/gam_api.c 16 Aug 2006 15:42:44 -0000 1.40 ++++ libgamin/gam_api.c 5 Sep 2006 14:02:43 -0000 +@@ -87,6 +87,13 @@ void + gam_show_debug(void) { + } + ++void gam_got_signal(void); ++ ++void ++gam_got_signal(void) { ++} ++ ++ + /************************************************************************ + * * + * Path for the socket connection * +Index: server/gam_connection.c +=================================================================== +RCS file: /cvs/gnome/gamin/server/gam_connection.c,v +retrieving revision 1.29 +diff -u -p -r1.29 gam_connection.c +--- server/gam_connection.c 22 Sep 2005 08:09:03 -0000 1.29 ++++ server/gam_connection.c 5 Sep 2006 14:02:43 -0000 +@@ -42,6 +42,9 @@ struct GamConnData { + guint eq_source; /* the event queue GSource id */ + }; + ++static void gam_cancel_server_timeout (void); ++ ++ + static const char * + gam_reqtype_to_string (GAMReqType type) + { +@@ -126,6 +129,9 @@ gam_connection_close(GamConnDataPtr conn + g_free(conn->pidname); + g_free(conn); + ++ if (gamConnList == NULL && gam_server_use_timeout ()) ++ gam_schedule_server_timeout (); ++ + return (0); + } + +@@ -196,6 +202,8 @@ gam_connection_new(GMainLoop *loop, GIOC + ret->eq_source = g_timeout_add (100 /* ms */, gam_connection_eq_flush, ret); + gamConnList = g_list_prepend(gamConnList, ret); + ++ gam_cancel_server_timeout (); ++ + GAM_DEBUG(DEBUG_INFO, "Created connection %d\n", ret->fd); + + return (ret); +@@ -683,7 +691,9 @@ gam_send_ack(GamConnDataPtr conn, int re + * * + ************************************************************************/ + +-#define MAX_IDLE_TIMEOUT 30 ++#define MAX_IDLE_TIMEOUT_MSEC (30*1000) /* 30 seconds */ ++ ++static guint server_timeout_id = 0; + + /** + * gam_connections_check: +@@ -692,27 +702,33 @@ gam_send_ack(GamConnDataPtr conn, int re + * shuts the server down if there have been no outstanding connections + * for a while. + */ +-gboolean ++static gboolean + gam_connections_check(void) + { +- static time_t timeout; +- +- if (g_list_first(gamConnList) != NULL) { +- if (timeout != 0) { +- GAM_DEBUG(DEBUG_INFO, "New active connection\n"); +- } +- timeout = 0; +- return (TRUE); +- } +- if (timeout == 0) { +- GAM_DEBUG(DEBUG_INFO, "No more active connections\n"); +- timeout = time(NULL); +- } else if (time(NULL) - timeout > MAX_IDLE_TIMEOUT) { ++ server_timeout_id = 0; ++ ++ if (gamConnList == NULL) { + GAM_DEBUG(DEBUG_INFO, "Exiting on timeout\n"); + gam_shutdown(); + exit(0); + } +- return (TRUE); ++ return (FALSE); ++} ++ ++static void ++gam_cancel_server_timeout (void) ++{ ++ if (server_timeout_id) ++ g_source_remove (server_timeout_id); ++ server_timeout_id = 0; ++} ++ ++void ++gam_schedule_server_timeout (void) ++{ ++ gam_cancel_server_timeout (); ++ server_timeout_id = ++ g_timeout_add(MAX_IDLE_TIMEOUT_MSEC, (GSourceFunc) gam_connections_check, NULL); + } + + /** +Index: server/gam_connection.h +=================================================================== +RCS file: /cvs/gnome/gamin/server/gam_connection.h,v +retrieving revision 1.6 +diff -u -p -r1.6 gam_connection.h +--- server/gam_connection.h 4 Aug 2005 16:45:25 -0000 1.6 ++++ server/gam_connection.h 5 Sep 2006 14:02:43 -0000 +@@ -25,7 +25,7 @@ typedef enum { + + int gam_connections_init (void); + int gam_connections_close (void); +-gboolean gam_connections_check (void); ++void gam_schedule_server_timeout (void); + + GamConnDataPtr gam_connection_new (GMainLoop *loop, + GIOChannel *source); +Index: server/gam_server.c +=================================================================== +RCS file: /cvs/gnome/gamin/server/gam_server.c,v +retrieving revision 1.42 +diff -u -p -r1.42 gam_server.c +--- server/gam_server.c 8 Sep 2005 10:12:03 -0000 1.42 ++++ server/gam_server.c 5 Sep 2006 14:02:44 -0000 +@@ -279,6 +279,17 @@ static GHashTable *listeners = NULL; + static GIOChannel *socket = NULL; + + /** ++ * gam_server_use_timeout: ++ * ++ * Returns TRUE if idle server should exit after a timeout. ++ */ ++gboolean ++gam_server_use_timeout (void) ++{ ++ return !no_timeout; ++} ++ ++/** + * gam_server_emit_one_event: + * @path: the file/directory path + * @event: the event type +@@ -507,6 +519,58 @@ gam_poll_file (GamNode *node) + return 0; + } + ++#ifdef GAM_DEBUG_ENABLED ++ ++static GIOChannel *pipe_read_ioc = NULL; ++static GIOChannel *pipe_write_ioc = NULL; ++ ++static gboolean ++gam_error_signal_pipe_handler(gpointer user_data) ++{ ++ char buf[5000]; ++ ++ if (pipe_read_ioc) ++ g_io_channel_read_chars(pipe_read_ioc, buf, sizeof(buf), NULL, NULL); ++ ++ gam_error_check(); ++} ++ ++static void ++gam_setup_error_handler (void) ++{ ++ int signal_pipe[2]; ++ GSource *source; ++ ++ if (pipe(signal_pipe) != -1) { ++ pipe_read_ioc = g_io_channel_unix_new(signal_pipe[0]); ++ pipe_write_ioc = g_io_channel_unix_new(signal_pipe[1]); ++ ++ g_io_channel_set_flags(pipe_read_ioc, G_IO_FLAG_NONBLOCK, NULL); ++ g_io_channel_set_flags(pipe_write_ioc, G_IO_FLAG_NONBLOCK, NULL); ++ ++ source = g_io_create_watch(pipe_read_ioc, G_IO_IN | G_IO_HUP | G_IO_ERR); ++ g_source_set_callback(source, gam_error_signal_pipe_handler, NULL, NULL); ++ ++ g_source_attach(source, NULL); ++ g_source_unref(source); ++ } ++} ++#endif ++ ++void ++gam_got_signal() ++{ ++#ifdef GAM_DEBUG_ENABLED ++ /* Wake up main loop */ ++ if (pipe_write_ioc) { ++ g_io_channel_write_chars(pipe_write_ioc, "a", 1, NULL, NULL); ++ g_io_channel_flush(pipe_write_ioc, NULL); ++ } ++#endif ++} ++ ++ ++ + /** + * gam_server_init: + * @loop: the main event loop of the daemon +@@ -533,11 +597,11 @@ gam_server_init(GMainLoop * loop, const + * Register the timeout checking function + */ + if (no_timeout == 0) +- g_timeout_add(1000, (GSourceFunc) gam_connections_check, NULL); ++ gam_schedule_server_timeout (); + #ifdef GAM_DEBUG_ENABLED +- g_timeout_add(1000, (GSourceFunc) gam_error_check, NULL); ++ gam_setup_error_handler (); + #endif +- ++ + return TRUE; + } + +Index: server/gam_server.h +=================================================================== +RCS file: /cvs/gnome/gamin/server/gam_server.h,v +retrieving revision 1.11 +diff -u -p -r1.11 gam_server.h +--- server/gam_server.h 16 Aug 2005 20:44:17 -0000 1.11 ++++ server/gam_server.h 5 Sep 2006 14:02:44 -0000 +@@ -33,6 +33,7 @@ typedef enum pollHandlerMode { + } pollHandlerMode; + + gboolean gam_init_subscriptions (void); ++gboolean gam_server_use_timeout (void); + gboolean gam_add_subscription (GamSubscription *sub); + gboolean gam_remove_subscription (GamSubscription *sub); + int gam_server_num_listeners (void); +@@ -48,6 +49,7 @@ void gam_server_emit_event + int force); + void gam_shutdown (void); + void gam_show_debug (void); ++void gam_got_signal (void); + + void gam_server_install_kernel_hooks (GamKernelHandler name, + gboolean (*add)(GamSubscription *sub), diff --git a/app-admin/gamin/files/gamin-stdin-devnull.patch b/app-admin/gamin/files/gamin-stdin-devnull.patch new file mode 100644 index 0000000..77c078c --- /dev/null +++ b/app-admin/gamin/files/gamin-stdin-devnull.patch @@ -0,0 +1,36 @@ +Index: libgamin/gam_fork.c +=================================================================== +RCS file: /cvs/gnome/gamin/libgamin/gam_fork.c,v +retrieving revision 1.11 +diff -u -p -r1.11 gam_fork.c +--- libgamin/gam_fork.c 27 Oct 2005 10:58:50 -0000 1.11 ++++ libgamin/gam_fork.c 22 Aug 2006 14:59:11 -0000 +@@ -67,6 +67,7 @@ gamin_fork_server(const char *fam_client + /* Become a daemon */ + pid = fork(); + if (pid == 0) { ++ int fd; + long open_max; + long i; + +@@ -75,6 +76,20 @@ gamin_fork_server(const char *fam_client + for (i = 0; i < open_max; i++) + fcntl (i, F_SETFD, FD_CLOEXEC); + ++ /* /dev/null for stdin, stdout, stderr */ ++ fd = open ("/dev/null", O_RDONLY); ++ if (fd != -1) { ++ dup2 (fd, 0); ++ close (fd); ++ } ++ ++ fd = open ("/dev/null", O_WRONLY); ++ if (fd != -1) { ++ dup2 (fd, 1); ++ dup2 (fd, 2); ++ close (fd); ++ } ++ + setsid(); + if (fork() == 0) { + #ifdef HAVE_SETENV diff --git a/app-admin/gamin/files/gamin-timer-on-demand.patch b/app-admin/gamin/files/gamin-timer-on-demand.patch new file mode 100644 index 0000000..3375bd5 --- /dev/null +++ b/app-admin/gamin/files/gamin-timer-on-demand.patch @@ -0,0 +1,73 @@ +--- gamin-0.1.7/server/gam_eq.c.timer-on-demand 2005-08-05 11:57:48.000000000 +0200 ++++ gamin-0.1.7/server/gam_eq.c 2006-09-05 10:57:06.000000000 +0200 +@@ -119,9 +119,10 @@ + gam_eq_event_free (event); + } + +-void ++gboolean + gam_eq_flush (gam_eq_t *eq, GamConnDataPtr conn) + { ++ gboolean done_work = FALSE; + if (!eq) + return; + +@@ -130,8 +131,10 @@ + #endif + while (!g_queue_is_empty (eq->event_queue)) + { ++ done_work = TRUE; + gam_eq_event_t *event = g_queue_pop_head (eq->event_queue); + g_assert (event); + gam_eq_flush_callback (eq, event, conn); + } ++ return done_work; + } +--- gamin-0.1.7/server/gam_connection.c.timer-on-demand 2005-09-22 10:10:55.000000000 +0200 ++++ gamin-0.1.7/server/gam_connection.c 2006-09-05 10:57:06.000000000 +0200 +@@ -159,12 +159,15 @@ + static gboolean + gam_connection_eq_flush (gpointer data) + { ++ gboolean work; + GamConnDataPtr conn = (GamConnDataPtr)data; + if (!conn) + return FALSE; + +- gam_eq_flush (conn->eq, conn); +- return TRUE; ++ work = gam_eq_flush (conn->eq, conn); ++ if (!work) ++ conn->eq_source = 0; ++ return work; + } + + /** +@@ -193,7 +196,7 @@ + ret->loop = loop; + ret->source = source; + ret->eq = gam_eq_new (); +- ret->eq_source = g_timeout_add (100 /* ms */, gam_connection_eq_flush, ret); ++ ret->eq_source = g_timeout_add (100 /* 100 milisecond */, gam_connection_eq_flush, ret); + gamConnList = g_list_prepend(gamConnList, ret); + + GAM_DEBUG(DEBUG_INFO, "Created connection %d\n", ret->fd); +@@ -620,6 +623,8 @@ + g_assert (conn->eq); + + gam_eq_queue (conn->eq, reqno, event, path, len); ++ if (!conn->eq_source) ++ conn->eq_source = g_timeout_add (100 /* 100 milisecond */, gam_connection_eq_flush, conn); + } + + +--- gamin-0.1.7/server/gam_eq.h.timer-on-demand 2006-09-05 11:00:12.000000000 +0200 ++++ gamin-0.1.7/server/gam_eq.h 2006-09-05 11:00:46.000000000 +0200 +@@ -9,6 +9,6 @@ + void gam_eq_free (gam_eq_t *eq); + void gam_eq_queue (gam_eq_t *eq, int reqno, int event, const char *path, int len); + guint gam_eq_size (gam_eq_t *eq); +-void gam_eq_flush (gam_eq_t *eq, GamConnDataPtr conn); ++gboolean gam_eq_flush (gam_eq_t *eq, GamConnDataPtr conn); + + #endif diff --git a/app-admin/gamin/gamin-0.1.7.ebuild b/app-admin/gamin/gamin-0.1.7.ebuild new file mode 100644 index 0000000..eea8abe --- /dev/null +++ b/app-admin/gamin/gamin-0.1.7.ebuild @@ -0,0 +1,73 @@ +# Copyright 1999-2006 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Header: /var/cvsroot/gentoo-x86/app-admin/gamin/gamin-0.1.7.ebuild,v 1.15 2006/03/31 23:53:04 flameeyes Exp $ + +inherit autotools eutils libtool + +DESCRIPTION="Library providing the FAM File Alteration Monitor API" +HOMEPAGE="http://www.gnome.org/~veillard/gamin/" +SRC_URI="http://www.gnome.org/~veillard/gamin/sources/${P}.tar.gz + kernel_FreeBSD? ( mirror://gentoo/${P}-freebsd.patch.bz2 )" + +LICENSE="LGPL-2" +SLOT="0" +KEYWORDS="alpha amd64 arm hppa ia64 m68k mips ppc ppc64 s390 sh sparc x86 ~x86-fbsd" +IUSE="debug doc" + +RDEPEND=">=dev-libs/glib-2 + !app-admin/fam" + +DEPEND="${RDEPEND} + dev-util/pkgconfig" + +PROVIDE="virtual/fam" + +src_unpack() { + unpack ${A} + + cd ${S} + + # Patches from Fedora Core 6 - http://cvs.fedora.redhat.com/viewcvs/rpms/gamin/devel/gamin.spec?rev=1.53&view=auto + # They should be all upstream now, but "cvs diff -rTAG -Ddate" hates me and loaning these separate patches is easier + epatch "${FILESDIR}/${PN}-dont-close-fd.patch" + epatch "${FILESDIR}/${PN}-stdin-devnull.patch" + epatch "${FILESDIR}/${PN}-flush-buffer-on-reset.patch" + epatch "${FILESDIR}/${PN}-0.1.7-sigaction.patch" + epatch "${FILESDIR}/${PN}-new-inotify-backend.patch" + epatch "${FILESDIR}/${PN}-inotify-fix.patch" + epatch "${FILESDIR}/${PN}-timer-on-demand.patch" + epatch "${FILESDIR}/${PN}-no-timers.patch" + + use kernel_FreeBSD && epatch "${DISTDIR}/${P}-freebsd.patch.bz2" + + # Do not remove + elibtoolize + + # New inotify backend changes Makefile.am (maybe others do too) + eautoreconf +} + +src_compile() { + econf \ + $(use_enable kernel_linux inotify) \ + $(use_enable debug) \ + $(use_enable debug debug-api) \ + || die "econf failed" + + emake || die "emake failed" +} + +src_install() { + make DESTDIR="${D}" install || die + + dodoc AUTHORS ChangeLog README TODO NEWS doc/*txt + use doc && dohtml doc/* +} + +pkg_postinst() { + if use kernel_linux; then + einfo "It is strongly suggested you use Gamin with an inotify enabled" + einfo "kernel for best performance. For this release of gamin you need" + einfo "at least gentoo-sources-2.6.13 or later." + fi +} -- cgit v1.2.3-65-gdbad