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 |
+/** @} */ |