* When a file monitored with kqueue is moved or removed, gamin does
not monitor it anymore. The patch fixes the issue by adding moved/removed files to the exist_list, so that gamin detects when they are recreated * No CHANGED event is emitted for the files contained in a monitored directory, because kqueue can't do that. The patch adds periodic polling for these files * Instead of calling kevent() every second, the patch uses an I/O watch (g_io_add_watch()) PR: 79605 Submitted by: Jean-Yves Lefort <jylefort@brutele.be>
This commit is contained in:
parent
40bda7dba8
commit
179b70aa16
Notes:
svn2git
2021-03-31 03:12:20 +00:00
svn path=/head/; revision=132661
2 changed files with 185 additions and 101 deletions
|
@ -7,7 +7,7 @@
|
|||
|
||||
PORTNAME= gamin
|
||||
PORTVERSION= 0.0.26
|
||||
PORTREVISION?= 8
|
||||
PORTREVISION?= 9
|
||||
CATEGORIES?= devel
|
||||
MASTER_SITES= http://www.gnome.org/~veillard/gamin/sources/
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
--- server/gam_kqueue.c.orig Thu Mar 31 20:39:54 2005
|
||||
+++ server/gam_kqueue.c Fri Apr 1 01:09:11 2005
|
||||
@@ -0,0 +1,636 @@
|
||||
--- server/gam_kqueue.c.orig Wed Apr 6 22:46:40 2005
|
||||
+++ server/gam_kqueue.c Wed Apr 6 22:47:16 2005
|
||||
@@ -0,0 +1,720 @@
|
||||
+/*
|
||||
+ * Copyright (C) 2005 Joe Marcus Clarke <marcus@FreeBSD.org>
|
||||
+ * Copyright (C) 2005 Jean-Yves Lefort <jylefort@brutele.be>
|
||||
+ *
|
||||
+ * This library is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -22,6 +23,7 @@
|
|||
+
|
||||
+#include <config.h>
|
||||
+#include <sys/types.h>
|
||||
+#include <sys/stat.h>
|
||||
+#include <sys/event.h>
|
||||
+#include <sys/time.h>
|
||||
+#include <fcntl.h>
|
||||
|
@ -47,6 +49,23 @@
|
|||
+ GSList *dirlist;
|
||||
+} KQueueData;
|
||||
+
|
||||
+typedef struct
|
||||
+{
|
||||
+ ino_t ino;
|
||||
+ mode_t mode;
|
||||
+ uid_t uid;
|
||||
+ gid_t gid;
|
||||
+ time_t mtime;
|
||||
+ time_t ctime;
|
||||
+ off_t size;
|
||||
+} MiniStat;
|
||||
+
|
||||
+typedef struct {
|
||||
+ char *pathname;
|
||||
+ char *filename; /* pointer into pathname */
|
||||
+ MiniStat sb;
|
||||
+} FileData;
|
||||
+
|
||||
+static GHashTable *dir_path_hash = NULL;
|
||||
+static GHashTable *file_path_hash = NULL;
|
||||
+static GHashTable *fd_hash = NULL;
|
||||
|
@ -80,70 +99,43 @@
|
|||
+ return data;
|
||||
+}
|
||||
+
|
||||
+static GSList *
|
||||
+gam_kqueue_lsdir(const char *path)
|
||||
+static void
|
||||
+gam_kqueue_mini_stat (const char *pathname, MiniStat *mini_sb)
|
||||
+{
|
||||
+ GDir *dir;
|
||||
+ GSList *lst = NULL;
|
||||
+ const gchar *entry;
|
||||
+ struct stat sb;
|
||||
+
|
||||
+ if (!path)
|
||||
+ return NULL;
|
||||
+
|
||||
+ dir = g_dir_open(path, 0, NULL);
|
||||
+ if (!dir)
|
||||
+ return NULL;
|
||||
+
|
||||
+ entry = g_dir_read_name(dir);
|
||||
+
|
||||
+ while (entry) {
|
||||
+ lst = g_slist_prepend(lst, g_strdup(entry));
|
||||
+ entry = g_dir_read_name(dir);
|
||||
+ if (lstat(pathname, &sb) == 0) {
|
||||
+ mini_sb->ino = sb.st_ino;
|
||||
+ mini_sb->mode = sb.st_mode;
|
||||
+ mini_sb->uid = sb.st_uid;
|
||||
+ mini_sb->gid = sb.st_gid;
|
||||
+ mini_sb->mtime = sb.st_mtime;
|
||||
+ mini_sb->ctime = sb.st_ctime;
|
||||
+ mini_sb->size = sb.st_size;
|
||||
+ } else {
|
||||
+ memset(mini_sb, 0, sizeof(*mini_sb));
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+ g_dir_close(dir);
|
||||
+static FileData *
|
||||
+gam_kqueue_file_data_new (const char *path, const char *filename)
|
||||
+{
|
||||
+ FileData *fdata;
|
||||
+
|
||||
+ return lst;
|
||||
+ fdata = g_new(FileData, 1);
|
||||
+ fdata->pathname = g_build_filename(path, filename, NULL);
|
||||
+ fdata->filename = strrchr(fdata->pathname, G_DIR_SEPARATOR);
|
||||
+ fdata->filename = fdata->filename ? fdata->filename + 1 : fdata->pathname;
|
||||
+ gam_kqueue_mini_stat(fdata->pathname, &fdata->sb);
|
||||
+
|
||||
+ return fdata;
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+gam_kqueue_cmplst(GSList *lst1, GSList *lst2, GSList **added, GSList **deleted)
|
||||
+gam_kqueue_file_data_free (FileData *fdata)
|
||||
+{
|
||||
+ int found;
|
||||
+ GSList *l;
|
||||
+
|
||||
+ if (!lst1 && !lst2)
|
||||
+ return;
|
||||
+
|
||||
+ if (!lst1) {
|
||||
+ *added = g_slist_copy(lst2);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (!lst2) {
|
||||
+ *deleted = g_slist_copy(lst1);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ for (l = lst1; l; l = l->next) {
|
||||
+ found = 0;
|
||||
+ if (g_slist_find_custom(lst2, l->data, (GCompareFunc)strcmp)) {
|
||||
+ found = 1;
|
||||
+ }
|
||||
+ if (found == 0) {
|
||||
+ *deleted = g_slist_prepend(*deleted, l->data);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ for (l = lst2; l; l = l->next) {
|
||||
+ found = 0;
|
||||
+ if (g_slist_find_custom(lst1, l->data, (GCompareFunc)strcmp)) {
|
||||
+ found = 1;
|
||||
+ }
|
||||
+ if (found == 0) {
|
||||
+ *added = g_slist_prepend(*added, l->data);
|
||||
+ }
|
||||
+ }
|
||||
+ g_free(fdata->pathname);
|
||||
+ g_free(fdata);
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
|
@ -151,7 +143,7 @@
|
|||
+{
|
||||
+ g_free(data->path);
|
||||
+ if (data->dirlist) {
|
||||
+ g_slist_foreach(data->dirlist, (GFunc)g_free, NULL);
|
||||
+ g_slist_foreach(data->dirlist, (GFunc)gam_kqueue_file_data_free, NULL);
|
||||
+ g_slist_free(data->dirlist);
|
||||
+ }
|
||||
+ if (data->subs) {
|
||||
|
@ -160,6 +152,12 @@
|
|||
+ g_free(data);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+gam_kqueue_dirlist_find (FileData *fdata, const char *filename)
|
||||
+{
|
||||
+ return strcmp(fdata->filename, filename);
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+gam_kqueue_add_rm_handler(const char *path, GamSubscription *sub, gboolean added, gboolean was_missing)
|
||||
+{
|
||||
|
@ -230,21 +228,29 @@
|
|||
+ gam_server_emit_event (path, isdir, GAMIN_EVENT_CREATED, subs, 1);
|
||||
+ }
|
||||
+ if (gam_subscription_is_dir(sub) && isdir) {
|
||||
+ GSList *l;
|
||||
+ GDir *dir;
|
||||
+
|
||||
+ data->isdir = TRUE;
|
||||
+ data->dirlist = gam_kqueue_lsdir(path);
|
||||
+ data->dirlist = NULL;
|
||||
+
|
||||
+ for (l = data->dirlist; l; l = l->next) {
|
||||
+ char *tmpentry;
|
||||
+ dir = g_dir_open(path, 0, NULL);
|
||||
+ if (dir) {
|
||||
+ const char *entry;
|
||||
+
|
||||
+ tmpentry = g_build_filename(path, l->data, NULL);
|
||||
+ if (!was_missing) {
|
||||
+ gam_server_emit_event (tmpentry,
|
||||
+ g_file_test(tmpentry, G_FILE_TEST_IS_DIR),
|
||||
+ GAMIN_EVENT_EXISTS, subs, 1);
|
||||
+ while ((entry = g_dir_read_name(dir))) {
|
||||
+ FileData *fdata;
|
||||
+
|
||||
+ fdata = gam_kqueue_file_data_new(path, entry);
|
||||
+ data->dirlist = g_slist_prepend(data->dirlist, fdata);
|
||||
+
|
||||
+ if (!was_missing) {
|
||||
+ gam_server_emit_event(fdata->pathname,
|
||||
+ g_file_test(fdata->pathname, G_FILE_TEST_IS_DIR),
|
||||
+ GAMIN_EVENT_EXISTS, subs, 1);
|
||||
+ }
|
||||
+ }
|
||||
+ g_free(tmpentry);
|
||||
+
|
||||
+ g_dir_close(dir);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
|
@ -332,44 +338,50 @@
|
|||
+ isdir = g_file_test(data->path, G_FILE_TEST_IS_DIR);
|
||||
+
|
||||
+ if (gevent == GAMIN_EVENT_CHANGED && data->isdir) {
|
||||
+ GSList *dirlist = NULL, *added = NULL, *deleted = NULL;
|
||||
+ GSList *dirlist = NULL;
|
||||
+ GSList *l;
|
||||
+ GDir *dir;
|
||||
+
|
||||
+ dirlist = gam_kqueue_lsdir(data->path);
|
||||
+ gam_kqueue_cmplst(data->dirlist, dirlist, &added, &deleted);
|
||||
+ if (added || deleted) {
|
||||
+ for (l = deleted; l; l = l->next) {
|
||||
+ data->dirlist = g_slist_remove(data->dirlist, l->data);
|
||||
+ event_path = g_build_filename(data->path, l->data, NULL);
|
||||
+ g_free(l->data);
|
||||
+ isdir = g_file_test(event_path, G_FILE_TEST_IS_DIR);
|
||||
+ dir = g_dir_open(data->path, 0, NULL);
|
||||
+ if (dir) {
|
||||
+ const char *entry;
|
||||
+
|
||||
+ GAM_DEBUG(DEBUG_INFO, "kqueue emitting event %s for %s\n", gam_event_to_string(GAMIN_EVENT_DELETED) , event_path);
|
||||
+
|
||||
+ gam_server_emit_event (event_path, isdir,
|
||||
+ GAMIN_EVENT_DELETED, data->subs, 1);
|
||||
+ g_free(event_path);
|
||||
+ while ((entry = g_dir_read_name(dir))) {
|
||||
+ dirlist = g_slist_prepend(dirlist, g_strdup(entry));
|
||||
+ }
|
||||
+
|
||||
+ for (l = added; l; l = l->next) {
|
||||
+ dirlist = g_slist_remove(dirlist, l->data);
|
||||
+ data->dirlist = g_slist_prepend(data->dirlist,
|
||||
+ g_strdup(l->data));
|
||||
+ event_path = g_build_filename(data->path, l->data, NULL);
|
||||
+ g_free(l->data);
|
||||
+ isdir = g_file_test(event_path, G_FILE_TEST_IS_DIR);
|
||||
+ g_dir_close(dir);
|
||||
+ }
|
||||
+
|
||||
+ GAM_DEBUG(DEBUG_INFO, "kqueue emitting event %s for %s\n", gam_event_to_string(GAMIN_EVENT_CREATED) , event_path);
|
||||
+ for (l = dirlist; l; l = l->next) {
|
||||
+ if (! g_slist_find_custom(data->dirlist, l->data, (GCompareFunc) gam_kqueue_dirlist_find)) {
|
||||
+ FileData *fdata;
|
||||
+
|
||||
+ gam_server_emit_event (event_path, isdir,
|
||||
+ GAMIN_EVENT_CREATED, data->subs, 1);
|
||||
+ g_free(event_path);
|
||||
+ fdata = gam_kqueue_file_data_new(data->path, l->data);
|
||||
+ data->dirlist = g_slist_prepend(data->dirlist, fdata);
|
||||
+
|
||||
+ GAM_DEBUG(DEBUG_INFO, "kqueue emitting event %s for %s\n", gam_event_to_string(GAMIN_EVENT_CREATED), fdata->pathname);
|
||||
+ gam_server_emit_event(fdata->pathname,
|
||||
+ g_file_test(fdata->pathname, G_FILE_TEST_IS_DIR),
|
||||
+ GAMIN_EVENT_CREATED, data->subs, 1);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (added)
|
||||
+ g_slist_free(added);
|
||||
+ if (deleted)
|
||||
+ g_slist_free(deleted);
|
||||
+ iterate:
|
||||
+ for (l = data->dirlist; l; l = l->next) {
|
||||
+ FileData *fdata = l->data;
|
||||
+
|
||||
+ if (! g_slist_find_custom(dirlist, fdata->filename, (GCompareFunc) strcmp)) {
|
||||
+ data->dirlist = g_slist_remove(data->dirlist, fdata);
|
||||
+
|
||||
+ GAM_DEBUG(DEBUG_INFO, "kqueue emitting event %s for %s\n", gam_event_to_string(GAMIN_EVENT_DELETED), fdata->pathname);
|
||||
+ gam_server_emit_event(fdata->pathname,
|
||||
+ g_file_test(fdata->pathname, G_FILE_TEST_IS_DIR),
|
||||
+ GAMIN_EVENT_DELETED, data->subs, 1);
|
||||
+
|
||||
+ gam_kqueue_file_data_free(fdata);
|
||||
+ goto iterate; /* list changed, start again */
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (dirlist) {
|
||||
|
@ -380,6 +392,22 @@
|
|||
+ }
|
||||
+ else {
|
||||
+ event_path = g_strdup (data->path);
|
||||
+
|
||||
+ if (gevent == GAMIN_EVENT_DELETED
|
||||
+ || gevent == GAMIN_EVENT_ENDEXISTS
|
||||
+ || gevent == GAMIN_EVENT_MOVED) {
|
||||
+ /* close and move to exist_list, to catch next creation */
|
||||
+ close(data->fd);
|
||||
+ if (data->isdir) {
|
||||
+ g_hash_table_remove(dir_path_hash, data->path);
|
||||
+ }
|
||||
+ else {
|
||||
+ g_hash_table_remove(file_path_hash, data->path);
|
||||
+ }
|
||||
+ g_hash_table_remove(fd_hash, GINT_TO_POINTER(data->fd));
|
||||
+
|
||||
+ exist_list = g_slist_append(exist_list, data);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ isdir = g_file_test(event_path, G_FILE_TEST_IS_DIR);
|
||||
|
@ -418,8 +446,51 @@
|
|||
+ return TRUE;
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+gam_kqueue_dirlist_check_cb (const char *path, KQueueData *data, gpointer user_data)
|
||||
+{
|
||||
+ GSList *l;
|
||||
+
|
||||
+ for (l = data->dirlist; l; l = l->next) {
|
||||
+ FileData *fdata = l->data;
|
||||
+ MiniStat sb;
|
||||
+
|
||||
+ gam_kqueue_mini_stat(fdata->pathname, &sb);
|
||||
+
|
||||
+ if (sb.mtime != fdata->sb.mtime
|
||||
+ || sb.ctime != fdata->sb.ctime
|
||||
+ || sb.size != fdata->sb.size
|
||||
+ || sb.mode != fdata->sb.mode
|
||||
+ || sb.uid != fdata->sb.uid
|
||||
+ || sb.gid != fdata->sb.gid
|
||||
+ || sb.ino != fdata->sb.ino)
|
||||
+ {
|
||||
+ memcpy(&fdata->sb, &sb, sizeof(sb));
|
||||
+
|
||||
+ GAM_DEBUG(DEBUG_INFO, "kqueue emitting event %s for %s\n", gam_event_to_string(GAMIN_EVENT_CHANGED), fdata->pathname);
|
||||
+ gam_server_emit_event(fdata->pathname,
|
||||
+ g_file_test(fdata->pathname, G_FILE_TEST_IS_DIR),
|
||||
+ GAMIN_EVENT_CHANGED, data->subs, 1);
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static gboolean
|
||||
+gam_kqueue_event_handler (gpointer user_data)
|
||||
+gam_kqueue_dirlist_check (gpointer user_data)
|
||||
+{
|
||||
+ G_LOCK(kqueue);
|
||||
+
|
||||
+ GAM_DEBUG(DEBUG_INFO, "gam_kqueue_dirlist_check()\n");
|
||||
+
|
||||
+ g_hash_table_foreach(dir_path_hash, (GHFunc) gam_kqueue_dirlist_check_cb, NULL);
|
||||
+
|
||||
+ G_UNLOCK(kqueue);
|
||||
+
|
||||
+ return TRUE;
|
||||
+}
|
||||
+
|
||||
+static gboolean
|
||||
+gam_kqueue_event_handler (GIOChannel *source, GIOCondition condition, gpointer user_data)
|
||||
+{
|
||||
+ KQueueData *data;
|
||||
+ struct kevent ev[1];
|
||||
|
@ -531,6 +602,8 @@
|
|||
+gboolean
|
||||
+gam_kqueue_init(void)
|
||||
+{
|
||||
+ GIOChannel *channel;
|
||||
+
|
||||
+ kq = kqueue();
|
||||
+ if (kq == -1) {
|
||||
+ GAM_DEBUG(DEBUG_INFO, "Could not initialize a new kqueue\n");
|
||||
|
@ -538,12 +611,23 @@
|
|||
+ }
|
||||
+
|
||||
+ g_timeout_add(1000, gam_kqueue_exist_check, NULL);
|
||||
+ g_timeout_add(1000, gam_kqueue_event_handler, NULL);
|
||||
+
|
||||
+ channel = g_io_channel_unix_new(kq);
|
||||
+ g_io_add_watch(channel, G_IO_IN, gam_kqueue_event_handler, NULL);
|
||||
+
|
||||
+ dir_path_hash = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
+ file_path_hash = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
+ fd_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
|
||||
+
|
||||
+ /*
|
||||
+ * gam_kqueue_dirlist_check() has to lstat() every file in every
|
||||
+ * monitored directory. This can easily become an intensive task
|
||||
+ * if a few large directories are monitored (for instance a mail
|
||||
+ * checker monitoring a couple of MH folders), therefore we use a
|
||||
+ * reasonable poll interval (6 seconds, same as FAM's default).
|
||||
+ */
|
||||
+ g_timeout_add(6000, gam_kqueue_dirlist_check, NULL);
|
||||
+
|
||||
+ GAM_DEBUG(DEBUG_INFO, "kqueue initialized\n");
|
||||
+
|
||||
+ gam_backend_add_subscription = gam_kqueue_add_subscription;
|
||||
|
|
Loading…
Reference in a new issue