View | Details | Raw Unified | Return to bug 79680
Collapse All | Expand All

(-)gamin/Makefile (-16 / +2 lines)
Lines 7-13 Link Here
7
7
8
PORTNAME=	gamin
8
PORTNAME=	gamin
9
PORTVERSION=	0.0.26
9
PORTVERSION=	0.0.26
10
PORTREVISION?=	9
10
PORTREVISION?=	10
11
CATEGORIES?=	devel
11
CATEGORIES?=	devel
12
MASTER_SITES=	http://www.gnome.org/~veillard/gamin/sources/
12
MASTER_SITES=	http://www.gnome.org/~veillard/gamin/sources/
13
13
Lines 25-45 Link Here
25
25
26
CONFLICTS=	fam-[0-9]*
26
CONFLICTS=	fam-[0-9]*
27
27
28
.if !defined(GAMIN_SLAVE)
29
OPTIONS=	KQUEUE "Enable the KQueue backend (UFS only)" on
30
.endif
31
32
.include <bsd.port.pre.mk>
33
34
.if !defined(GAMIN_SLAVE)
35
.if defined(WITHOUT_KQUEUE)
36
CONFIGURE_ARGS+=	--disable-kqueue
37
.else
38
CONFIGURE_ARGS+=	--enable-kqueue
39
.endif
40
.endif
41
42
post-patch:
28
post-patch:
43
	@${FIND} ${WRKSRC} -type f | ${XARGS} ${TOUCH} -f
29
	@${FIND} ${WRKSRC} -type f | ${XARGS} ${TOUCH} -f
44
30
45
.include <bsd.port.post.mk>
31
.include <bsd.port.mk>
(-)gamin/files/patch-server_gam_kqueue.c (-533 / +503 lines)
Lines 1-6 Link Here
1
--- server/gam_kqueue.c.orig	Wed Apr  6 22:46:40 2005
1
--- server/gam_kqueue.c.orig	Fri Apr  8 14:50:41 2005
2
+++ server/gam_kqueue.c	Wed Apr  6 22:47:16 2005
2
+++ server/gam_kqueue.c	Fri Apr  8 14:51:01 2005
3
@@ -0,0 +1,720 @@
3
@@ -0,0 +1,690 @@
4
+/*
4
+/*
5
+ * Copyright (C) 2005 Joe Marcus Clarke <marcus@FreeBSD.org>
5
+ * Copyright (C) 2005 Joe Marcus Clarke <marcus@FreeBSD.org>
6
+ * Copyright (C) 2005 Jean-Yves Lefort <jylefort@brutele.be>
6
+ * Copyright (C) 2005 Jean-Yves Lefort <jylefort@brutele.be>
Lines 20-640 Link Here
20
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
+ */
21
+ */
22
+
22
+
23
+
23
+#include "config.h"
24
+#include <config.h>
24
+#include <string.h>
25
+#include <fcntl.h>
26
+#include <unistd.h>
25
+#include <sys/types.h>
27
+#include <sys/types.h>
26
+#include <sys/stat.h>
28
+#include <sys/stat.h>
27
+#include <sys/event.h>
29
+#include <sys/event.h>
28
+#include <sys/time.h>
30
+#include <sys/time.h>
29
+#include <fcntl.h>
31
+#include <errno.h>
30
+#include <sys/ioctl.h>
31
+#include <signal.h>
32
+#include <unistd.h>
33
+#include <stdio.h>
34
+#include <string.h>
35
+#include <glib.h>
36
+#include "gam_error.h"
32
+#include "gam_error.h"
37
+#include "gam_kqueue.h"
33
+#include "gam_kqueue.h"
38
+#include "gam_tree.h"
39
+#include "gam_event.h"
34
+#include "gam_event.h"
40
+#include "gam_server.h"
35
+#include "gam_server.h"
41
+#include "gam_event.h"
42
+
36
+
43
+typedef struct {
37
+#define VN_NOTE_ALL	(NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | \
44
+    char *path;
38
+			 NOTE_ATTRIB | NOTE_LINK | NOTE_RENAME | \
45
+    int fd;
39
+			 NOTE_REVOKE)
46
+    int refcount;
40
+#define VN_NOTE_CHANGED	(NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_LINK)
47
+    gboolean isdir;
41
+#define VN_NOTE_DELETED (NOTE_DELETE | NOTE_REVOKE)
48
+    GList *subs;
49
+    GSList *dirlist;
50
+} KQueueData;
51
+
42
+
52
+typedef struct
43
+typedef struct
53
+{
44
+{
54
+    ino_t ino;
45
+  gboolean	exists;
55
+    mode_t mode;
46
+  ino_t		ino;
56
+    uid_t uid;
47
+  mode_t	mode;
57
+    gid_t gid;
48
+  uid_t		uid;
58
+    time_t mtime;
49
+  gid_t		gid;
59
+    time_t ctime;
50
+  time_t	mtime;
60
+    off_t size;
51
+  time_t	ctime;
52
+  off_t		size;
61
+} MiniStat;
53
+} MiniStat;
62
+
54
+
63
+typedef struct {
55
+typedef struct
64
+    char *pathname;
56
+{
65
+    char *filename;		/* pointer into pathname */
57
+  const char	*path;		/* belongs to the first sub in subs */
66
+    MiniStat sb;
58
+  GList		*subs;
67
+} FileData;
59
+  GSList	*files;		/* list of files in directory */
68
+  
60
+  MiniStat	sb;		/* for poll */
69
+static GHashTable *dir_path_hash = NULL;
61
+  int		fd;		/* for kqueue */
70
+static GHashTable *file_path_hash = NULL;
62
+  gboolean	isdir;		/* is a directory monitor? */
71
+static GHashTable *fd_hash = NULL;
63
+} Monitor;
72
+
73
+static GSList *exist_list = NULL;
74
+
64
+
75
+static GList *new_subs = NULL;
65
+typedef struct
76
+G_LOCK_DEFINE_STATIC(new_subs);
66
+{
77
+static GList *removed_subs = NULL;
67
+  char		*pathname;
78
+G_LOCK_DEFINE_STATIC(removed_subs);
68
+  char		*filename;	/* pointer into pathname */
69
+  MiniStat	sb;
70
+} FileEntry;
79
+
71
+
80
+G_LOCK_DEFINE_STATIC(kqueue);
72
+typedef struct
73
+{
74
+  GSourceFunc	func;
75
+  unsigned int	interval;
76
+  unsigned int	timeout_id;
77
+  GSList	*monitors;
78
+} Poller;
81
+
79
+
82
+static gboolean have_consume_idler = FALSE;
80
+static int kq = -1;
83
+
81
+
84
+int kq = -1;
82
+static GHashTable *dir_hash = NULL;
83
+static GHashTable *file_hash = NULL;
85
+
84
+
86
+static KQueueData *
85
+/*
87
+gam_kqueue_data_new(const char *path, int fd)
86
+ * The poller monitoring file creations. Low usage is expected,
88
+{
87
+ * therefore we set a small interval (one second).
89
+    KQueueData *data;
88
+ */
89
+static gboolean gam_kqueue_missing_poll (gpointer user_data);
90
+static Poller missing_poller = { gam_kqueue_missing_poll, 1000, -1, NULL };
90
+
91
+
91
+    data = g_new0(KQueueData, 1);
92
+/*
92
+    data->path = g_strdup(path);
93
+ * The poller monitoring files not supported by kqueue (remote files,
93
+    data->fd = fd;
94
+ * etc). Very low usage is expected, but since this poller is likely
94
+    data->refcount = 1;
95
+ * going to access the network, we set a medium interval (3 seconds).
95
+    data->isdir = FALSE;
96
+ */
96
+    data->subs = NULL;
97
+static gboolean gam_kqueue_unsupported_poll (gpointer user_data);
97
+    data->dirlist = NULL;
98
+static Poller unsupported_poller = { gam_kqueue_unsupported_poll, 3000, -1, NULL };
98
+
99
+
99
+    return data;
100
+/*
100
+}
101
+ * The poller monitoring files inside monitored directories. Very high
102
+ * usage is expected (a mail checker monitoring a couple of large MH
103
+ * folders will lead to thousands of lstat() calls for each check),
104
+ * therefore we set a large interval (6 seconds, same as FAM's
105
+ * default).
106
+ */
107
+static gboolean gam_kqueue_dirs_poll (gpointer user_data);
108
+static Poller dirs_poller = { gam_kqueue_dirs_poll, 6000, -1, NULL };
101
+
109
+
102
+static void
110
+static void
103
+gam_kqueue_mini_stat (const char *pathname, MiniStat *mini_sb)
111
+gam_kqueue_mini_lstat (const char *pathname, MiniStat *mini_sb)
104
+{
112
+{
105
+    struct stat sb;
113
+  struct stat sb;
106
+
114
+  
107
+    if (lstat(pathname, &sb) == 0) {
115
+  if (lstat(pathname, &sb) < 0)
108
+        mini_sb->ino = sb.st_ino;
116
+    memset(mini_sb, 0, sizeof(*mini_sb));
109
+	mini_sb->mode = sb.st_mode;
117
+  else
110
+	mini_sb->uid = sb.st_uid;
118
+    {
111
+	mini_sb->gid = sb.st_gid;
119
+      mini_sb->exists = TRUE;
112
+        mini_sb->mtime = sb.st_mtime;
120
+      mini_sb->ino = sb.st_ino;
113
+	mini_sb->ctime = sb.st_ctime;
121
+      mini_sb->mode = sb.st_mode;
114
+	mini_sb->size = sb.st_size;
122
+      mini_sb->uid = sb.st_uid;
115
+    } else {
123
+      mini_sb->gid = sb.st_gid;
116
+        memset(mini_sb, 0, sizeof(*mini_sb));
124
+      mini_sb->mtime = sb.st_mtime;
125
+      mini_sb->ctime = sb.st_ctime;
126
+      mini_sb->size = sb.st_size;
117
+    }
127
+    }
118
+}
128
+}
119
+
129
+
120
+static FileData *
130
+static FileEntry *
121
+gam_kqueue_file_data_new (const char *path, const char *filename)
131
+gam_kqueue_file_entry_new (const char *path, const char *filename)
122
+{
132
+{
123
+    FileData *fdata;
133
+  FileEntry *entry;
124
+
125
+    fdata = g_new(FileData, 1);
126
+    fdata->pathname = g_build_filename(path, filename, NULL);
127
+    fdata->filename = strrchr(fdata->pathname, G_DIR_SEPARATOR);
128
+    fdata->filename = fdata->filename ? fdata->filename + 1 : fdata->pathname;
129
+    gam_kqueue_mini_stat(fdata->pathname, &fdata->sb);
130
+
134
+
131
+    return fdata;
135
+  entry = g_new(FileEntry, 1);
136
+  entry->pathname = g_build_filename(path, filename, NULL);
137
+  entry->filename = strrchr(entry->pathname, G_DIR_SEPARATOR);
138
+  entry->filename = entry->filename ? entry->filename + 1 : entry->pathname;
139
+  gam_kqueue_mini_lstat(entry->pathname, &entry->sb);
140
+  
141
+  return entry;
132
+}
142
+}
133
+
143
+
134
+static void
144
+static void
135
+gam_kqueue_file_data_free (FileData *fdata)
145
+gam_kqueue_file_entry_free (FileEntry *entry)
136
+{
146
+{
137
+    g_free(fdata->pathname);
147
+  g_free(entry->pathname);
138
+    g_free(fdata);
148
+  g_free(entry);
139
+}
149
+}
140
+
150
+
141
+static void
151
+static gboolean
142
+gam_kqueue_data_free(KQueueData * data)
152
+gam_kqueue_differs (const MiniStat *sb1, const MiniStat *sb2)
143
+{
153
+{
144
+    g_free(data->path);
154
+  return sb1->mtime != sb2->mtime
145
+    if (data->dirlist) {
155
+    || sb1->ctime != sb2->ctime
146
+        g_slist_foreach(data->dirlist, (GFunc)gam_kqueue_file_data_free, NULL);
156
+    || sb1->size != sb2->size
147
+        g_slist_free(data->dirlist);
157
+    || sb1->mode != sb2->mode
148
+    }
158
+    || sb1->uid != sb2->uid
149
+    if (data->subs) {
159
+    || sb1->gid != sb2->gid
150
+	g_list_free(data->subs);
160
+    || sb1->ino != sb2->ino;
151
+    }
152
+    g_free(data);
153
+}
161
+}
154
+
162
+
155
+static int
163
+static int
156
+gam_kqueue_dirlist_find (FileData *fdata, const char *filename)
164
+gam_kqueue_files_find (const FileEntry *entry, const char *filename)
157
+{
165
+{
158
+    return strcmp(fdata->filename, filename);
166
+    return strcmp(entry->filename, filename);
159
+}
167
+}
160
+
168
+
161
+static void
169
+static void
162
+gam_kqueue_add_rm_handler(const char *path, GamSubscription *sub, gboolean added, gboolean was_missing)
170
+gam_kqueue_poller_add_monitor (Poller *poller, Monitor *mon)
163
+{
171
+{
164
+    KQueueData *data;
172
+  if (! g_slist_find(poller->monitors, mon))
165
+    struct kevent ev[1];
173
+    {
166
+    int isdir = 0;
174
+      poller->monitors = g_slist_prepend(poller->monitors, mon);
167
+    int fd;
175
+      if (poller->timeout_id == -1)
168
+
176
+	poller->timeout_id = g_timeout_add(poller->interval, poller->func, NULL);
169
+    G_LOCK(kqueue);
170
+
171
+    isdir = g_file_test(path, G_FILE_TEST_IS_DIR);
172
+    if (gam_subscription_is_dir(sub)) {
173
+        data = g_hash_table_lookup(dir_path_hash, path);
174
+    }
175
+    else {
176
+        data = g_hash_table_lookup(file_path_hash, path);
177
+    }
177
+    }
178
+}
178
+
179
+
179
+    if (added) {
180
+static void
180
+	GList *subs;
181
+gam_kqueue_poller_remove_monitor (Poller *poller, Monitor *mon)
181
+
182
+{
182
+	subs = NULL;
183
+  poller->monitors = g_slist_remove(poller->monitors, mon);
183
+	subs = g_list_append(subs, sub);
184
+  if (! poller->monitors && poller->timeout_id != -1)
184
+
185
+    {
185
+        if (data != NULL) {
186
+      g_source_remove(poller->timeout_id);
186
+            data->refcount++;
187
+      poller->timeout_id = -1;
187
+	    data->subs = g_list_prepend(data->subs, sub);
188
+            G_UNLOCK(kqueue);
189
+	    GAM_DEBUG(DEBUG_INFO, "kqueue updated refcount\n");
190
+	    if (!was_missing) {
191
+	        gam_server_emit_event (path, isdir, GAMIN_EVENT_EXISTS, subs, 1);
192
+                gam_server_emit_event (path, isdir, GAMIN_EVENT_ENDEXISTS, subs, 1);
193
+	    }
194
+	    else {
195
+                gam_server_emit_event (path, isdir, GAMIN_EVENT_CREATED, subs, 1);
196
+	    }
197
+            return;
198
+        }
199
+
200
+	if (!g_file_test(path, G_FILE_TEST_EXISTS)) {
201
+	    data = gam_kqueue_data_new(path, -1);
202
+	    data->subs = g_list_prepend(data->subs, sub);
203
+            exist_list = g_slist_append(exist_list, data);
204
+	    gam_server_emit_event (path, isdir, GAMIN_EVENT_DELETED, subs, 1);
205
+	    gam_server_emit_event (path, isdir, GAMIN_EVENT_ENDEXISTS, subs, 1);
206
+	    G_UNLOCK(kqueue);
207
+	    return;
208
+	}
209
+
210
+	fd = open(path, O_RDONLY);
211
+
212
+	if (fd < 0) {
213
+            G_UNLOCK(kqueue);
214
+	    return;
215
+	}
216
+
217
+        EV_SET(ev, fd, EVFILT_VNODE,
218
+            EV_ADD | EV_ENABLE | EV_CLEAR, VN_NOTE_ALL, 0, 0);
219
+        kevent(kq, ev, 1, NULL, 0, NULL);
220
+
221
+        data = gam_kqueue_data_new(path, fd);
222
+        data->subs = g_list_prepend(data->subs, sub);
223
+
224
+	if (!was_missing) {
225
+            gam_server_emit_event (path, isdir, GAMIN_EVENT_EXISTS, subs, 1);
226
+	}
227
+	else {
228
+            gam_server_emit_event (path, isdir, GAMIN_EVENT_CREATED, subs, 1);
229
+	}
230
+        if (gam_subscription_is_dir(sub) && isdir) {
231
+	    GDir *dir;
232
+
233
+            data->isdir = TRUE;
234
+	    data->dirlist = NULL;
235
+
236
+	    dir = g_dir_open(path, 0, NULL);
237
+	    if (dir) {
238
+	        const char *entry;
239
+
240
+		while ((entry = g_dir_read_name(dir))) {
241
+		    FileData *fdata;
242
+
243
+		    fdata = gam_kqueue_file_data_new(path, entry);
244
+		    data->dirlist = g_slist_prepend(data->dirlist, fdata);
245
+
246
+		    if (!was_missing) {
247
+		        gam_server_emit_event(fdata->pathname,
248
+					      g_file_test(fdata->pathname, G_FILE_TEST_IS_DIR),
249
+					      GAMIN_EVENT_EXISTS, subs, 1);
250
+		    }
251
+		}
252
+
253
+		g_dir_close(dir);
254
+	    }
255
+	}
256
+
257
+	if (!was_missing) {
258
+	    gam_server_emit_event (path, isdir, GAMIN_EVENT_ENDEXISTS, subs, 1);
259
+	}
260
+
261
+        g_hash_table_insert(fd_hash, GINT_TO_POINTER(data->fd), data);
262
+	if (data->isdir) {
263
+            g_hash_table_insert(dir_path_hash, data->path, data);
264
+	}
265
+	else {
266
+            g_hash_table_insert(file_path_hash, data->path, data);
267
+	}
268
+
269
+	if (subs)
270
+            g_list_free(subs);
271
+
272
+        GAM_DEBUG(DEBUG_INFO, "added kqueue watch for %s\n", path);
273
+    } else {
274
+
275
+        if (!data) {
276
+            G_UNLOCK(kqueue);
277
+            return;
278
+        }
279
+
280
+	if (g_list_find (data->subs, sub)) {
281
+		data->subs = g_list_remove_all (data->subs, sub);
282
+	}
283
+        data->refcount--;
284
+	    GAM_DEBUG(DEBUG_INFO, "kqueue decremeneted refcount for %s\n", path);
285
+
286
+        if (data->refcount == 0) {
287
+            GList *l;
288
+
289
+	    close(data->fd);
290
+            l = data->subs;
291
+            for (l = l; l; l = l->next) {
292
+                GamSubscription *sub = l->data;
293
+                gam_kqueue_remove_subscription (sub);
294
+            }
295
+            GAM_DEBUG(DEBUG_INFO, "removed kqueue watch for %s\n", data->path);
296
+	    if (data->isdir) {
297
+                g_hash_table_remove(dir_path_hash, data->path);
298
+	    }
299
+	    else {
300
+		g_hash_table_remove(file_path_hash, data->path);
301
+	    }
302
+            g_hash_table_remove(fd_hash, GINT_TO_POINTER(data->fd));
303
+            gam_kqueue_data_free(data);
304
+        }
305
+    }
188
+    }
306
+    G_UNLOCK(kqueue);
307
+}
189
+}
308
+
190
+
309
+static GaminEventType kqueue_event_to_gamin_event (int mask)
191
+static void
192
+gam_kqueue_monitor_clear_files (Monitor *mon)
310
+{
193
+{
311
+	if ((mask & VN_NOTE_CHANGED) != 0)
194
+  gam_kqueue_poller_remove_monitor(&dirs_poller, mon);
312
+		return GAMIN_EVENT_CHANGED;
195
+  g_slist_foreach(mon->files, (GFunc) gam_kqueue_file_entry_free, NULL);
313
+	else if ((mask & NOTE_DELETE) != 0)
196
+  g_slist_free(mon->files);
314
+		return GAMIN_EVENT_DELETED;
197
+  mon->files = NULL;
315
+	else if ((mask & NOTE_REVOKE) != 0)
316
+		return GAMIN_EVENT_ENDEXISTS;
317
+	else if ((mask & NOTE_RENAME) != 0)
318
+		return GAMIN_EVENT_MOVED;
319
+	else
320
+		return GAMIN_EVENT_UNKNOWN;
321
+}
198
+}
322
+
199
+
323
+static void gam_kqueue_emit_event (KQueueData *data, struct kevent *event)
200
+static void
201
+gam_kqueue_monitor_set_missing (Monitor *mon)
324
+{
202
+{
325
+	GaminEventType gevent;
203
+  if (mon->fd >= 0)
326
+	int isdir = 0;
204
+    {
327
+	char *event_path;
205
+      close(mon->fd);
206
+      mon->fd = -1;
207
+    }
328
+
208
+
329
+	if (!data||!event)
209
+  /*
330
+		return;
210
+   * This shouldn't normally happen, since a directory cannot be
211
+   * deleted unless all its files are deleted first.
212
+   */
213
+  if (mon->files)
214
+    gam_kqueue_monitor_clear_files(mon);
331
+
215
+
332
+	gevent = kqueue_event_to_gamin_event (event->fflags);
216
+  gam_kqueue_poller_remove_monitor(&unsupported_poller, mon);
217
+  gam_kqueue_poller_add_monitor(&missing_poller, mon);
218
+}
333
+
219
+
334
+	if (gevent == GAMIN_EVENT_UNKNOWN) {
220
+static void
335
+		return;
221
+gam_kqueue_monitor_set_unsupported (Monitor *mon)
336
+	}
222
+{
223
+  if (mon->fd >= 0)
224
+    {
225
+      close(mon->fd);
226
+      mon->fd = -1;
227
+    }
337
+
228
+
338
+	isdir = g_file_test(data->path, G_FILE_TEST_IS_DIR);
229
+  gam_kqueue_mini_lstat(mon->path, &mon->sb);
230
+  gam_kqueue_poller_add_monitor(&unsupported_poller, mon);
231
+}
339
+
232
+
340
+	if (gevent == GAMIN_EVENT_CHANGED && data->isdir) {
233
+static void
341
+	    GSList *dirlist = NULL;
234
+gam_kqueue_monitor_enable_notification (Monitor *mon, gboolean was_missing)
342
+	    GSList *l;
235
+{
343
+	    GDir *dir;
236
+  struct stat sb;
344
+
237
+  gboolean exists;
345
+	    dir = g_dir_open(data->path, 0, NULL);
238
+  gboolean isdir;
346
+	    if (dir) {
239
+  struct kevent ev[1];
347
+	        const char *entry;
348
+
240
+
349
+		while ((entry = g_dir_read_name(dir))) {
241
+  /* we first send CREATED or EXISTS/DELETED+ENDEXISTS events */
350
+		    dirlist = g_slist_prepend(dirlist, g_strdup(entry));
351
+		}
352
+
242
+
353
+		g_dir_close(dir);
243
+  exists = lstat(mon->path, &sb) >= 0;
244
+  isdir = exists && (sb.st_mode & S_IFDIR) != 0;
245
+  
246
+  if (exists)
247
+    {
248
+      GaminEventType gevent;
249
+
250
+      gevent = was_missing ? GAMIN_EVENT_CREATED : GAMIN_EVENT_EXISTS;
251
+      gam_server_emit_event(mon->path, isdir, gevent, mon->subs, 1);
252
+
253
+      if (mon->isdir && isdir)
254
+	{
255
+	  GDir *dir;
256
+	  GError *err = NULL;
257
+	  
258
+	  if (mon->files)
259
+	    {
260
+	      /* be robust, handle the bug gracefully */
261
+	      GAM_DEBUG(DEBUG_INFO, "there is a bug in gam_kqueue: monitor had files\n");
262
+	      gam_kqueue_monitor_clear_files(mon);
354
+	    }
263
+	    }
355
+
264
+	  
356
+	    for (l = dirlist; l; l = l->next) {
265
+	  dir = g_dir_open(mon->path, 0, &err);
357
+	        if (! g_slist_find_custom(data->dirlist, l->data, (GCompareFunc) gam_kqueue_dirlist_find)) {
266
+	  if (dir)
358
+		    FileData *fdata;
267
+	    {
359
+
268
+	      const char *filename;
360
+		    fdata = gam_kqueue_file_data_new(data->path, l->data);
269
+	      
361
+		    data->dirlist = g_slist_prepend(data->dirlist, fdata);
270
+	      while ((filename = g_dir_read_name(dir)))
362
+
271
+		{
363
+		    GAM_DEBUG(DEBUG_INFO, "kqueue emitting event %s for %s\n", gam_event_to_string(GAMIN_EVENT_CREATED), fdata->pathname);
272
+		  FileEntry *entry;
364
+		    gam_server_emit_event(fdata->pathname,
273
+		  
365
+					  g_file_test(fdata->pathname, G_FILE_TEST_IS_DIR),
274
+		  entry = gam_kqueue_file_entry_new(mon->path, filename);
366
+					  GAMIN_EVENT_CREATED, data->subs, 1);
275
+		  mon->files = g_slist_prepend(mon->files, entry);
276
+		  
277
+		  gam_server_emit_event(entry->pathname,
278
+					(entry->sb.mode & S_IFDIR) != 0,
279
+					gevent, mon->subs, 1);
367
+		}
280
+		}
281
+	      
282
+	      g_dir_close(dir);
368
+	    }
283
+	    }
369
+
284
+	  else
370
+	iterate:
285
+	    {
371
+	    for (l = data->dirlist; l; l = l->next) {
286
+	      GAM_DEBUG(DEBUG_INFO, "unable to open directory %s: %s\n", mon->path, err->message);
372
+	        FileData *fdata = l->data;
287
+	      g_error_free(err);
373
+
374
+	        if (! g_slist_find_custom(dirlist, fdata->filename, (GCompareFunc) strcmp)) {
375
+		    data->dirlist = g_slist_remove(data->dirlist, fdata);
376
+
377
+                    GAM_DEBUG(DEBUG_INFO, "kqueue emitting event %s for %s\n", gam_event_to_string(GAMIN_EVENT_DELETED), fdata->pathname);
378
+		    gam_server_emit_event(fdata->pathname,
379
+					  g_file_test(fdata->pathname, G_FILE_TEST_IS_DIR),
380
+					  GAMIN_EVENT_DELETED, data->subs, 1);
381
+
382
+		    gam_kqueue_file_data_free(fdata);
383
+		    goto iterate; /* list changed, start again */
384
+		}
385
+	    }
288
+	    }
386
+
289
+
387
+	    if (dirlist) {
290
+	  if (mon->files)
388
+	        g_slist_foreach(dirlist, (GFunc)g_free, NULL);
291
+	    gam_kqueue_poller_add_monitor(&dirs_poller, mon);
389
+	        g_slist_free(dirlist);
390
+	    }
391
+            return;
392
+	}
292
+	}
393
+	else {
394
+	    event_path = g_strdup (data->path);
395
+
396
+	    if (gevent == GAMIN_EVENT_DELETED
397
+		|| gevent == GAMIN_EVENT_ENDEXISTS
398
+		|| gevent == GAMIN_EVENT_MOVED) {
399
+	      /* close and move to exist_list, to catch next creation */
400
+	      close(data->fd);
401
+	      if (data->isdir) {
402
+		  g_hash_table_remove(dir_path_hash, data->path);
403
+	      }
404
+	      else {
405
+		  g_hash_table_remove(file_path_hash, data->path);
406
+	      }
407
+	      g_hash_table_remove(fd_hash, GINT_TO_POINTER(data->fd));
408
+
293
+
409
+	      exist_list = g_slist_append(exist_list, data);
294
+      if (! was_missing)
410
+	    }
295
+	gam_server_emit_event(mon->path, isdir, GAMIN_EVENT_ENDEXISTS, mon->subs, 1);
296
+    }
297
+  else
298
+    {
299
+      if (! was_missing)
300
+	{
301
+	  gam_server_emit_event(mon->path, isdir, GAMIN_EVENT_DELETED, mon->subs, 1);
302
+	  gam_server_emit_event(mon->path, isdir, GAMIN_EVENT_ENDEXISTS, mon->subs, 1);
411
+	}
303
+	}
412
+
304
+
413
+        isdir = g_file_test(event_path, G_FILE_TEST_IS_DIR);
305
+      gam_kqueue_monitor_set_missing(mon);
414
+
306
+      return;
415
+	GAM_DEBUG(DEBUG_INFO, "kqueue emitting event %s for %s\n", gam_event_to_string(gevent) , event_path);
307
+    }
308
+    
309
+  /* then we enable kqueue notification, falling back to poll if necessary */
416
+
310
+
417
+	gam_server_emit_event (event_path, isdir, gevent, data->subs, 1);
311
+  mon->fd = open(mon->path, O_RDONLY);
312
+  if (mon->fd < 0)
313
+    {
314
+      GAM_DEBUG(DEBUG_INFO, "cannot open %s (%s), falling back to poll\n", mon->path, g_strerror(errno));
315
+      gam_kqueue_monitor_set_unsupported(mon);
316
+      return;
317
+    }
418
+
318
+
419
+	g_free (event_path);
319
+  EV_SET(ev, mon->fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, VN_NOTE_ALL, 0, mon);
320
+  if (kevent(kq, ev, G_N_ELEMENTS(ev), NULL, 0, NULL) < 0)
321
+    {
322
+      GAM_DEBUG(DEBUG_INFO, "cannot enable kqueue notification for %s (%s), falling back to poll\n", mon->path, g_strerror(errno));
323
+      gam_kqueue_monitor_set_unsupported(mon);
324
+    }
420
+}
325
+}
421
+
326
+
422
+static gboolean
327
+static void
423
+gam_kqueue_exist_check (gpointer user_data)
328
+gam_kqueue_monitor_handle_directory_change (Monitor *mon)
424
+{
329
+{
425
+    GSList *l, *tmplst;
330
+  GSList *filenames = NULL;
426
+    KQueueData *data;
331
+  GSList *l;
332
+  GSList *tmp_files;
333
+  GDir *dir;
334
+  GError *err = NULL;
335
+
336
+  dir = g_dir_open(mon->path, 0, &err);
337
+  if (dir)
338
+    {
339
+      const char *filename;
427
+
340
+
428
+    tmplst = g_slist_copy(exist_list);
341
+      while ((filename = g_dir_read_name(dir)))
342
+	filenames = g_slist_prepend(filenames, g_strdup(filename));
429
+
343
+
430
+    for (l = tmplst; l; l = l->next) {
344
+      g_dir_close(dir);
431
+        data = l->data;
345
+    }
346
+  else
347
+    {
348
+      GAM_DEBUG(DEBUG_INFO, "unable to open directory %s: %s\n", mon->path, err->message);
349
+      g_error_free(err);
350
+    }
432
+
351
+
433
+	if (g_file_test(data->path, G_FILE_TEST_EXISTS)) {
352
+  /* handle created files */
434
+            /* The subs list is guaranteed to have only one entry. */
353
+  for (l = filenames; l; l = l->next)
435
+            GamSubscription *sub = data->subs->data;
354
+    if (! g_slist_find_custom(mon->files, l->data, (GCompareFunc) gam_kqueue_files_find))
355
+      {
356
+	FileEntry *entry;
357
+      
358
+	entry = gam_kqueue_file_entry_new(mon->path, l->data);
359
+	mon->files = g_slist_prepend(mon->files, entry);
360
+      
361
+	gam_server_emit_event(entry->pathname,
362
+			      (entry->sb.mode & S_IFDIR) != 0,
363
+			      GAMIN_EVENT_CREATED, mon->subs, 1);
364
+      }
365
+
366
+  /* handle deleted files */
367
+  tmp_files = g_slist_copy(mon->files);
368
+  for (l = tmp_files; l; l = l->next)
369
+    {
370
+      FileEntry *entry = l->data;
371
+
372
+      if (! g_slist_find_custom(filenames, entry->filename, (GCompareFunc) strcmp))
373
+	{
374
+	  mon->files = g_slist_remove(mon->files, entry);
375
+
376
+	  gam_server_emit_event(entry->pathname,
377
+				(entry->sb.mode & S_IFDIR) != 0,
378
+				GAMIN_EVENT_DELETED, mon->subs, 1);
436
+
379
+
437
+            exist_list = g_slist_remove(exist_list, data);
380
+	  gam_kqueue_file_entry_free(entry);
438
+	    gam_kqueue_add_rm_handler(data->path, sub, TRUE, TRUE);
439
+	    gam_kqueue_data_free(data);
440
+	}
381
+	}
441
+    }
382
+    }
383
+  g_slist_free(tmp_files);
442
+
384
+
443
+    if (tmplst)
385
+  if (filenames)
444
+        g_slist_free(tmplst);
386
+    {
387
+      g_slist_foreach(filenames, (GFunc) g_free, NULL);
388
+      g_slist_free(filenames);
389
+    }
445
+
390
+
446
+    return TRUE;
391
+  if (mon->files)
392
+    gam_kqueue_poller_add_monitor(&dirs_poller, mon);
393
+  else
394
+    gam_kqueue_poller_remove_monitor(&dirs_poller, mon);
447
+}
395
+}
448
+
396
+
449
+static void
397
+static void
450
+gam_kqueue_dirlist_check_cb (const char *path, KQueueData *data, gpointer user_data)
398
+gam_kqueue_monitor_emit_event (Monitor *mon,
451
+{
399
+			       GaminEventType event,
452
+    GSList *l;
400
+			       gboolean has_isdir,
453
+
401
+			       gboolean isdir)
454
+    for (l = data->dirlist; l; l = l->next) {
402
+{
455
+        FileData *fdata = l->data;
403
+  if (! has_isdir)
456
+	MiniStat sb;
404
+    {
405
+      struct stat sb;
406
+      isdir = lstat(mon->path, &sb) >= 0 && (sb.st_mode & S_IFDIR) != 0;
407
+    }
457
+
408
+
458
+	gam_kqueue_mini_stat(fdata->pathname, &sb);
409
+  switch (event)
410
+    {
411
+    case GAMIN_EVENT_CHANGED:
412
+      if (mon->isdir)
413
+	{
414
+	  gam_kqueue_monitor_handle_directory_change(mon);
415
+	  return;
416
+	}
417
+      break;
418
+
419
+    case GAMIN_EVENT_DELETED:
420
+    case GAMIN_EVENT_MOVED:
421
+      gam_kqueue_monitor_set_missing(mon);
422
+      break;
423
+    }
459
+
424
+
460
+	if (sb.mtime != fdata->sb.mtime
425
+  gam_server_emit_event(mon->path, isdir, event, mon->subs, 1);
461
+	    || sb.ctime != fdata->sb.ctime
426
+}
462
+	    || sb.size != fdata->sb.size
463
+	    || sb.mode != fdata->sb.mode
464
+	    || sb.uid != fdata->sb.uid
465
+	    || sb.gid != fdata->sb.gid
466
+	    || sb.ino != fdata->sb.ino)
467
+	  {
468
+	      memcpy(&fdata->sb, &sb, sizeof(sb));
469
+
427
+
470
+	      GAM_DEBUG(DEBUG_INFO, "kqueue emitting event %s for %s\n", gam_event_to_string(GAMIN_EVENT_CHANGED), fdata->pathname);
428
+static void
471
+	      gam_server_emit_event(fdata->pathname,
429
+gam_kqueue_monitor_handle_kevent (Monitor *mon, struct kevent *event)
472
+				    g_file_test(fdata->pathname, G_FILE_TEST_IS_DIR),
430
+{
473
+				    GAMIN_EVENT_CHANGED, data->subs, 1);
431
+  if ((event->flags & EV_ERROR) != 0)
474
+	  }
432
+    {
433
+      /* kqueue failed, fallback to poll */
434
+      GAM_DEBUG(DEBUG_INFO, "kqueue failed for %s, falling back to poll\n", mon->path);
435
+      gam_kqueue_monitor_set_unsupported(mon);
436
+      return;
475
+    }
437
+    }
438
+
439
+  /*
440
+   * kevent aggregates events, so we must handle a fflags with
441
+   * multiple event bits set.
442
+   */
443
+  if ((event->fflags & VN_NOTE_CHANGED) != 0)
444
+    gam_kqueue_monitor_emit_event(mon, GAMIN_EVENT_CHANGED, FALSE, FALSE);
445
+  if ((event->fflags & VN_NOTE_DELETED) != 0)
446
+    gam_kqueue_monitor_emit_event(mon, GAMIN_EVENT_DELETED, FALSE, FALSE);
447
+  if ((event->fflags & NOTE_RENAME) != 0)
448
+    gam_kqueue_monitor_emit_event(mon, GAMIN_EVENT_MOVED, FALSE, FALSE);
476
+}
449
+}
477
+
450
+
478
+static gboolean
451
+static gboolean
479
+gam_kqueue_dirlist_check (gpointer user_data)
452
+gam_kqueue_kevent_cb (GIOChannel *source, GIOCondition condition, gpointer user_data)
480
+{
453
+{
481
+    G_LOCK(kqueue);
454
+  int nevents;
482
+
455
+  struct kevent ev[1];
483
+    GAM_DEBUG(DEBUG_INFO, "gam_kqueue_dirlist_check()\n");
456
+  struct timespec timeout = { 0, 0 };
484
+
457
+  int i;
485
+    g_hash_table_foreach(dir_path_hash, (GHFunc) gam_kqueue_dirlist_check_cb, NULL);
458
+
459
+  nevents = kevent(kq, NULL, 0, ev, G_N_ELEMENTS(ev), &timeout);
460
+  if (nevents < 0)
461
+    {
462
+      GAM_DEBUG(DEBUG_INFO, "kevent() failure: %s\n", g_strerror(errno));
463
+      return TRUE;		/* keep source */
464
+    }
486
+
465
+
487
+    G_UNLOCK(kqueue);
466
+  for (i = 0; i < nevents; i++)
467
+    gam_kqueue_monitor_handle_kevent(ev[i].udata, &ev[i]);
488
+
468
+
489
+    return TRUE;
469
+  return TRUE;			/* keep source */
490
+}
470
+}
491
+
471
+
492
+static gboolean
472
+static gboolean
493
+gam_kqueue_event_handler (GIOChannel *source, GIOCondition condition, gpointer user_data)
473
+gam_kqueue_missing_poll (gpointer user_data)
494
+{
474
+{
495
+    KQueueData *data;
475
+  GSList *tmp_list;
496
+    struct kevent ev[1];
476
+  GSList *l;
497
+    struct timespec timeout = { 0, 0 };
498
+    int fd, i, nevents;
499
+
500
+    G_LOCK(kqueue);
501
+
502
+    GAM_DEBUG(DEBUG_INFO, "gam_kqueue_event_handler()\n");
503
+
504
+    nevents = kevent(kq, NULL, 0, ev, 1, &timeout);
505
+    if (nevents == -1)
506
+        return FALSE;
507
+    for (i = 0; i < nevents; i++) {
508
+        fd = ev[i].ident;
509
+
510
+	data = g_hash_table_lookup (fd_hash, GINT_TO_POINTER(fd));
511
+	if (!data) {
512
+	    GAM_DEBUG(DEBUG_INFO, "kqueue can't find fd %d\n", fd);
513
+	    GAM_DEBUG(DEBUG_INFO, "weird things have happened to kqueue.\n");
514
+	} else {
515
+	    gam_kqueue_emit_event (data, &ev[i]);
516
+	}
517
+
477
+
518
+    }
478
+  /* the list may be modified while we're iterating, so we use a copy */
519
+
479
+
520
+    G_UNLOCK(kqueue);
480
+  tmp_list = g_slist_copy(missing_poller.monitors);
481
+  for (l = tmp_list; l; l = l->next)
482
+    {
483
+      Monitor *mon = l->data;
484
+      struct stat sb;
485
+
486
+      if (lstat(mon->path, &sb) >= 0)
487
+	{
488
+	  gam_kqueue_poller_remove_monitor(&missing_poller, mon);
489
+	  gam_kqueue_monitor_enable_notification(mon, TRUE);
490
+	}
491
+    }
492
+  g_slist_free(tmp_list);
521
+
493
+
522
+    return TRUE;
494
+  return TRUE;
523
+}
495
+}
524
+
496
+
525
+static gboolean
497
+static gboolean
526
+gam_kqueue_consume_subscriptions_real(gpointer data)
498
+gam_kqueue_unsupported_poll (gpointer user_data)
527
+{
499
+{
528
+	GList *subs, *l;
500
+  GSList *tmp_list;
501
+  GSList *l;
529
+
502
+
530
+	G_LOCK(new_subs);
503
+  /* the list may be modified while we're iterating, so we use a copy */
531
+	if (new_subs) {
532
+		subs = new_subs;
533
+		new_subs = NULL;
534
+		G_UNLOCK(new_subs);
535
+
536
+		for (l = subs; l; l = l->next) {
537
+			GamSubscription *sub = l->data;
538
+			GAM_DEBUG(DEBUG_INFO, "called gam_kqueue_add_handler()\n");
539
+			gam_kqueue_add_rm_handler (gam_subscription_get_path (sub), sub, TRUE, FALSE);
540
+		}
541
+
542
+	} else {
543
+		G_UNLOCK(new_subs);
544
+	}
545
+
546
+	G_LOCK(removed_subs);
547
+	if (removed_subs) {
548
+		subs = removed_subs;
549
+		removed_subs = NULL;
550
+		G_UNLOCK(removed_subs);
551
+
552
+		for (l = subs; l; l = l->next) {
553
+			GamSubscription *sub = l->data;
554
+			GAM_DEBUG(DEBUG_INFO, "called gam_kqueue_rm_handler()\n");
555
+			gam_kqueue_add_rm_handler (gam_subscription_get_path (sub), sub, FALSE, FALSE);
556
+		}
557
+	} else {
558
+		G_UNLOCK(removed_subs);
559
+	}
560
+
504
+
561
+	GAM_DEBUG(DEBUG_INFO, "gam_kqueue_consume_subscriptions()\n");
505
+  tmp_list = g_slist_copy(unsupported_poller.monitors);
506
+  for (l = tmp_list; l; l = l->next)
507
+    {
508
+      Monitor *mon = l->data;
509
+      MiniStat sb;
510
+      GaminEventType event;
511
+
512
+      gam_kqueue_mini_lstat(mon->path, &sb);
513
+
514
+      if (! sb.exists && mon->sb.exists)
515
+	event = GAMIN_EVENT_DELETED;
516
+      else if (gam_kqueue_differs(&sb, &mon->sb))
517
+	event = GAMIN_EVENT_CHANGED;
518
+      else
519
+	continue;
520
+	
521
+      memcpy(&mon->sb, &sb, sizeof(sb));
522
+      gam_kqueue_monitor_emit_event(mon, event, TRUE, (sb.mode & S_IFDIR) != 0);
523
+    }
524
+  g_slist_free(tmp_list);
562
+
525
+
563
+	have_consume_idler = FALSE;
526
+  return TRUE;
564
+	return FALSE;
565
+}
527
+}
566
+
528
+
567
+static void
529
+static gboolean
568
+gam_kqueue_consume_subscriptions(void)
530
+gam_kqueue_dirs_poll (gpointer user_data)
569
+{
531
+{
570
+	GSource *source;
532
+  GSList *l;
571
+
572
+	if (have_consume_idler)
573
+		return;
574
+
533
+
575
+	have_consume_idler = TRUE;
534
+  /* the list cannot be modified while we're iterating */
576
+
535
+
577
+	source = g_idle_source_new ();
536
+  for (l = dirs_poller.monitors; l; l = l->next)
578
+	g_source_set_callback (source, gam_kqueue_consume_subscriptions_real, NULL, NULL);
537
+    {
538
+      Monitor *mon = l->data;
539
+      GSList *f;
540
+
541
+      for (f = mon->files; f; f = f->next)
542
+	{
543
+	  FileEntry *entry = f->data;
544
+	  MiniStat sb;
545
+
546
+	  gam_kqueue_mini_lstat(entry->pathname, &sb);
547
+	  if (gam_kqueue_differs(&sb, &entry->sb))
548
+	    {
549
+	      memcpy(&entry->sb, &sb, sizeof(sb));
550
+	      gam_server_emit_event(entry->pathname,
551
+				    (sb.mode & S_IFDIR) != 0,
552
+				    GAMIN_EVENT_CHANGED,
553
+				    mon->subs, 1);
554
+	    }
555
+	}
556
+    }
579
+
557
+
580
+	g_source_attach (source, NULL);
558
+  return TRUE;
581
+}
559
+}
582
+
560
+
583
+/**
561
+/**
584
+ * @defgroup kqueue kqueue backend
585
+ * @ingroup Backends
586
+ * @brief kqueue backend API
587
+ *
588
+ * Since 4.1, FreeBSD kernels have included the kernel event notification
589
+ * machanism (kqueue).  This backend uses kqueue to know when
590
+ * files are changed/created/deleted.
591
+ *
592
+ * @{
593
+ */
594
+
595
+
596
+/**
597
+ * Initializes the kqueue system.  This must be called before
562
+ * Initializes the kqueue system.  This must be called before
598
+ * any other functions in this module.
563
+ * any other functions in this module.
599
+ *
564
+ *
600
+ * @returns TRUE if initialization succeeded, FALSE otherwise
565
+ * @returns TRUE if initialization succeeded, FALSE otherwise
601
+ */
566
+ */
602
+gboolean
567
+gboolean
603
+gam_kqueue_init(void)
568
+gam_kqueue_init (void)
604
+{
569
+{
605
+    GIOChannel *channel;
570
+  GIOChannel *channel;
606
+
571
+
607
+    kq = kqueue();
572
+  kq = kqueue();
608
+    if (kq == -1) {
573
+  if (kq < 0)
609
+	GAM_DEBUG(DEBUG_INFO, "Could not initialize a new kqueue\n");
574
+    {
610
+	return FALSE;
575
+      gam_error(DEBUG_INFO, "kqueue initialization failure: %s\n", g_strerror(errno));
576
+      return FALSE;
611
+    }
577
+    }
612
+
578
+
613
+    g_timeout_add(1000, gam_kqueue_exist_check, NULL);
579
+  dir_hash = g_hash_table_new(g_str_hash, g_str_equal);
614
+
580
+  file_hash = g_hash_table_new(g_str_hash, g_str_equal);
615
+    channel = g_io_channel_unix_new(kq);
616
+    g_io_add_watch(channel, G_IO_IN, gam_kqueue_event_handler, NULL);
617
+
581
+
618
+    dir_path_hash = g_hash_table_new(g_str_hash, g_str_equal);
582
+  channel = g_io_channel_unix_new(kq);
619
+    file_path_hash = g_hash_table_new(g_str_hash, g_str_equal);
583
+  g_io_add_watch(channel, G_IO_IN, gam_kqueue_kevent_cb, NULL);
620
+    fd_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
621
+
584
+
622
+    /*
585
+  gam_backend_add_subscription = gam_kqueue_add_subscription;
623
+     * gam_kqueue_dirlist_check() has to lstat() every file in every
586
+  gam_backend_remove_subscription = gam_kqueue_remove_subscription;
624
+     * monitored directory. This can easily become an intensive task
587
+  gam_backend_remove_all_for = gam_kqueue_remove_all_for;
625
+     * if a few large directories are monitored (for instance a mail
626
+     * checker monitoring a couple of MH folders), therefore we use a
627
+     * reasonable poll interval (6 seconds, same as FAM's default).
628
+     */
629
+    g_timeout_add(6000, gam_kqueue_dirlist_check, NULL);
630
+
588
+
631
+    GAM_DEBUG(DEBUG_INFO, "kqueue initialized\n");
589
+  return TRUE;
632
+
633
+    gam_backend_add_subscription = gam_kqueue_add_subscription;
634
+    gam_backend_remove_subscription = gam_kqueue_remove_subscription;
635
+    gam_backend_remove_all_for = gam_kqueue_remove_all_for;
636
+
637
+    return TRUE;
638
+}
590
+}
639
+
591
+
640
+/**
592
+/**
Lines 644-661 Link Here
644
+ * @returns TRUE if adding the subscription succeeded, FALSE otherwise
596
+ * @returns TRUE if adding the subscription succeeded, FALSE otherwise
645
+ */
597
+ */
646
+gboolean
598
+gboolean
647
+gam_kqueue_add_subscription(GamSubscription * sub)
599
+gam_kqueue_add_subscription (GamSubscription *sub)
648
+{
600
+{
649
+	gam_listener_add_subscription(gam_subscription_get_listener(sub), sub);
601
+  const char *path;
650
+
602
+  GHashTable *hash;
651
+	G_LOCK(new_subs);
603
+  Monitor *mon;
652
+	new_subs = g_list_prepend(new_subs, sub);
604
+
653
+	G_UNLOCK(new_subs);
605
+  gam_listener_add_subscription(gam_subscription_get_listener(sub), sub);
606
+
607
+  path = gam_subscription_get_path(sub);
608
+  hash = gam_subscription_is_dir(sub) ? dir_hash : file_hash;
609
+  mon = g_hash_table_lookup(hash, path);
610
+
611
+  if (mon)
612
+    {
613
+      mon->subs = g_list_append(mon->subs, sub);
614
+      return TRUE;
615
+    }
616
+  
617
+  mon = g_new0(Monitor, 1);
618
+  mon->path = path;
619
+  mon->subs = g_list_append(mon->subs, sub);
620
+  mon->fd = -1;
621
+  mon->isdir = hash == dir_hash;
654
+
622
+
655
+	GAM_DEBUG(DEBUG_INFO, "kqueue_add_sub\n");
623
+  g_hash_table_insert(hash, (gpointer) mon->path, mon);
624
+  gam_kqueue_monitor_enable_notification(mon, FALSE);
656
+
625
+
657
+	gam_kqueue_consume_subscriptions();
626
+  return TRUE;
658
+    return TRUE;
659
+}
627
+}
660
+
628
+
661
+/**
629
+/**
Lines 665-692 Link Here
665
+ * @returns TRUE if removing the subscription succeeded, FALSE otherwise
633
+ * @returns TRUE if removing the subscription succeeded, FALSE otherwise
666
+ */
634
+ */
667
+gboolean
635
+gboolean
668
+gam_kqueue_remove_subscription(GamSubscription * sub)
636
+gam_kqueue_remove_subscription (GamSubscription *sub)
669
+{
637
+{
670
+	G_LOCK(new_subs);
638
+  GHashTable *hash;
671
+	if (g_list_find(new_subs, sub)) {
639
+  Monitor *mon;
672
+		GAM_DEBUG(DEBUG_INFO, "removed sub found on new_subs\n");
640
+
673
+		new_subs = g_list_remove_all (new_subs, sub);
641
+  hash = gam_subscription_is_dir(sub) ? dir_hash : file_hash;
674
+		G_UNLOCK(new_subs);
642
+  mon = g_hash_table_lookup(hash, gam_subscription_get_path(sub));
675
+		return TRUE;
643
+
676
+	}
644
+  if (! mon)
677
+	G_UNLOCK(new_subs);
645
+    return FALSE;
678
+
646
+
679
+	gam_subscription_cancel (sub);
647
+  mon->subs = g_list_remove_all(mon->subs, sub);
680
+	gam_listener_remove_subscription(gam_subscription_get_listener(sub), sub);
648
+  if (! mon->subs)
649
+    {
650
+      g_hash_table_remove(hash, mon->path);
681
+
651
+
682
+	G_LOCK(removed_subs);
652
+      /* might have been in any of these two */
683
+	removed_subs = g_list_prepend (removed_subs, sub);
653
+      gam_kqueue_poller_remove_monitor(&missing_poller, mon);
684
+	G_UNLOCK(removed_subs);
654
+      gam_kqueue_poller_remove_monitor(&unsupported_poller, mon);
685
+
655
+
686
+	GAM_DEBUG(DEBUG_INFO, "kqueue_remove_sub\n");
656
+      if (mon->fd >= 0)
687
+	gam_kqueue_consume_subscriptions();
657
+	close(mon->fd);
688
+
658
+
689
+    return TRUE;
659
+      if (mon->files)
660
+	gam_kqueue_monitor_clear_files(mon);
661
+
662
+      g_free(mon);
663
+    }
664
+
665
+  gam_subscription_cancel(sub);
666
+  gam_listener_remove_subscription(gam_subscription_get_listener(sub), sub);
667
+
668
+  return TRUE;
690
+}
669
+}
691
+
670
+
692
+/**
671
+/**
Lines 696-723 Link Here
696
+ * @returns TRUE if removing the subscriptions succeeded, FALSE otherwise
675
+ * @returns TRUE if removing the subscriptions succeeded, FALSE otherwise
697
+ */
676
+ */
698
+gboolean
677
+gboolean
699
+gam_kqueue_remove_all_for(GamListener * listener)
678
+gam_kqueue_remove_all_for (GamListener *listener)
700
+{
679
+{
701
+	GList *subs, *l = NULL;
680
+  GList *subs;
702
+
681
+  GList *l;
703
+	subs = gam_listener_get_subscriptions (listener);
682
+  gboolean success = TRUE;
704
+
683
+
705
+	for (l = subs; l; l = l->next) {
684
+  subs = gam_listener_get_subscriptions(listener);
706
+		GamSubscription *sub = l->data;
707
+
685
+
708
+		g_assert (sub != NULL);
686
+  for (l = subs; l; l = l->next)
687
+    if (! gam_kqueue_remove_subscription(l->data))
688
+      success = FALSE;
709
+
689
+
710
+		gam_kqueue_remove_subscription (sub);
690
+  g_list_free(subs);
711
+
691
+  
712
+	}
692
+  return success;
713
+
714
+	if (subs) {
715
+		g_list_free (subs);
716
+		gam_kqueue_consume_subscriptions();
717
+		return TRUE;
718
+	} else {
719
+		return FALSE;
720
+	}
721
+}
693
+}
722
+
723
+/** @} */
(-)gamin/files/patch-server_gam_kqueue.h (-11 / +3 lines)
Lines 1-19 Link Here
1
--- server/gam_kqueue.h.orig	Sat Mar 26 18:32:19 2005
1
--- server/gam_kqueue.h.orig	Fri Apr  8 14:50:41 2005
2
+++ server/gam_kqueue.h	Sat Mar 26 18:36:11 2005
2
+++ server/gam_kqueue.h	Fri Apr  8 14:51:01 2005
3
@@ -0,0 +1,24 @@
3
@@ -0,0 +1,16 @@
4
+
5
+#ifndef __MD_KQUEUE_H__
4
+#ifndef __MD_KQUEUE_H__
6
+#define __MD_KQUEUE_H__
5
+#define __MD_KQUEUE_H__
7
+
6
+
8
+#include <glib.h>
7
+#include <glib.h>
9
+#include "gam_poll.h"
10
+#include "gam_subscription.h"
8
+#include "gam_subscription.h"
11
+
12
+#define VN_NOTE_MOST	(NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | \
13
+			 NOTE_ATTRIB | NOTE_LINK | NOTE_RENAME | \
14
+			 NOTE_REVOKE)
15
+#define VN_NOTE_ALL	VN_NOTE_MOST
16
+#define VN_NOTE_CHANGED	(NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_LINK)
17
+
9
+
18
+G_BEGIN_DECLS
10
+G_BEGIN_DECLS
19
+
11
+
(-)gamin/files/patch-tests_testing.c (+14 lines)
Line 0 Link Here
1
--- tests/testing.c.orig	Fri Apr  8 15:09:45 2005
2
+++ tests/testing.c	Fri Apr  8 15:09:57 2005
3
@@ -424,9 +424,9 @@
4
             return (-1);
5
         }
6
         /*
7
-         * wait at most 3 secs before declaring failure
8
+         * wait at most 7 secs before declaring failure
9
          */
10
-        while ((delay < 30) && (testState.nb_events < nb_events + count)) {
11
+        while ((delay < 70) && (testState.nb_events < nb_events + count)) {
12
             debugLoop(100);
13
 
14
 /*	    printf("+"); fflush(stdout); */
(-)gamin/pkg-descr (+3 lines)
Lines 2-5 Link Here
2
FAM (File Alteration Monitor) system. This is a service provided by a library
2
FAM (File Alteration Monitor) system. This is a service provided by a library
3
which allows to detect when a file or a directory has been modified.
3
which allows to detect when a file or a directory has been modified.
4
4
5
Whereas the FreeBSD port of FAM polls files every few seconds, this port
6
includes a kqueue(2) backend for immediate notification of most alterations.
7
5
WWW: http://www.gnome.org/~veillard/gamin/index.html
8
WWW: http://www.gnome.org/~veillard/gamin/index.html

Return to bug 79680