e6d7d53f6f
keeping things here makes development easier. Bump PKGREVISION.
821 lines
20 KiB
C
821 lines
20 KiB
C
/***************************************************************************
|
|
* CVSID: $Id: hal-file-monitor.c,v 1.1 2008/11/25 23:10:23 jmcneill Exp $
|
|
*
|
|
* hal-file-monitor.c: Kqueue-based file monitor
|
|
*
|
|
* Copyright (C) 2007 Joe Marcus Clarke <marcus@FreeBSD.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
**************************************************************************/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/event.h>
|
|
#include <sys/time.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <glib.h>
|
|
#include <glib/gi18n.h>
|
|
#include <glib/gstdio.h>
|
|
#include <glib-object.h>
|
|
|
|
#include "../hal-file-monitor.h"
|
|
|
|
#define HAL_FILE_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), HAL_TYPE_FILE_MONITOR, HalFileMonitorPrivate))
|
|
|
|
#define VN_NOTE_CHANGED (NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_LINK)
|
|
#define VN_NOTE_DELETED (NOTE_DELETE | NOTE_REVOKE)
|
|
#define VN_NOTE_ALL (VN_NOTE_CHANGED | VN_NOTE_DELETED | NOTE_RENAME)
|
|
|
|
typedef struct
|
|
{
|
|
char *path;
|
|
int omask;
|
|
gboolean isdir;
|
|
HalFileMonitorNotifyFunc func;
|
|
gpointer udata;
|
|
GHashTable *dir_contents;
|
|
} FileKqueueData;
|
|
|
|
typedef struct
|
|
{
|
|
char *path;
|
|
time_t atime;
|
|
gboolean owner;
|
|
HalFileMonitorNotifyFunc func;
|
|
gpointer udata;
|
|
} FileAccessData;
|
|
|
|
struct HalFileMonitorPrivate
|
|
{
|
|
int kqueue_fd;
|
|
guint io_watch;
|
|
guint access_source;
|
|
|
|
gboolean initialized_kqueue;
|
|
|
|
GHashTable *fd_to_kdata;
|
|
GHashTable *fd_to_adata;
|
|
};
|
|
|
|
G_DEFINE_TYPE (HalFileMonitor, hal_file_monitor, G_TYPE_OBJECT)
|
|
|
|
static gpointer monitor_object = NULL;
|
|
|
|
static void hal_file_monitor_finalize (GObject *object);
|
|
static GHashTable *get_dir_contents (const char *path);
|
|
static GHashTable *diff_dir_contents (FileKqueueData *data, GSList **added, GSList **removed);
|
|
static int hal_mask_to_kmask (int mask);
|
|
static void monitor_release_kdata (HalFileMonitor *monitor, int fd, FileKqueueData *data);
|
|
static void monitor_release_adata (HalFileMonitor *monitor, int fd, FileAccessData *data);
|
|
static gboolean remove_kdata_foreach (gpointer fd, FileKqueueData *data, HalFileMonitor *monitor);
|
|
static gboolean remove_adata_foreach (gpointer fd, FileAccessData *data, HalFileMonitor *monitor);
|
|
static void hal_file_monitor_remove_kdata (HalFileMonitor *monitor, guint id);
|
|
static void hal_file_monitor_remove_adata (HalFileMonitor *monitor, guint id);
|
|
static void setup_monitor (HalFileMonitor *monitor);
|
|
static void close_monitor (HalFileMonitor *monitor);
|
|
static gboolean hal_file_access_monitor (gpointer data);
|
|
/*static char *fflags_to_str (int fflags);*/
|
|
static void emit_monitor_event (HalFileMonitor *monitor, HalFileMonitorEvent event, const char *path, HalFileMonitorNotifyFunc func, gpointer udata);
|
|
static gboolean handle_kqueue_event (GIOChannel *source, GIOCondition condition, gpointer udata);
|
|
|
|
GQuark
|
|
hal_file_monitor_error_quark (void)
|
|
{
|
|
static GQuark ret = 0;
|
|
if (ret == 0)
|
|
{
|
|
ret = g_quark_from_static_string ("hal_file_monitor_error");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
hal_mask_to_kmask (int mask)
|
|
{
|
|
int kmask = 0;
|
|
|
|
if (mask & HAL_FILE_MONITOR_EVENT_CREATE)
|
|
{
|
|
kmask |= NOTE_WRITE | NOTE_LINK;
|
|
}
|
|
|
|
if (mask & HAL_FILE_MONITOR_EVENT_DELETE)
|
|
{
|
|
kmask |= NOTE_WRITE | NOTE_LINK | VN_NOTE_DELETED;
|
|
}
|
|
|
|
if (mask & HAL_FILE_MONITOR_EVENT_CHANGE)
|
|
{
|
|
kmask |= VN_NOTE_CHANGED;
|
|
}
|
|
|
|
return kmask;
|
|
}
|
|
|
|
guint
|
|
hal_file_monitor_add_notify (HalFileMonitor *monitor,
|
|
const char *path,
|
|
int mask,
|
|
HalFileMonitorNotifyFunc notify_func,
|
|
gpointer data)
|
|
{
|
|
struct kevent ev;
|
|
struct stat sb;
|
|
int fd;
|
|
int id = 0;
|
|
|
|
if (! monitor->priv->initialized_kqueue)
|
|
{
|
|
return id;
|
|
}
|
|
|
|
fd = open (path, O_RDONLY);
|
|
if (fd < 0)
|
|
{
|
|
return id;
|
|
}
|
|
|
|
if (fstat (fd, &sb) == -1)
|
|
{
|
|
close (fd);
|
|
return id;
|
|
}
|
|
|
|
if (mask & HAL_FILE_MONITOR_EVENT_ACCESS)
|
|
{
|
|
FileAccessData *adata;
|
|
|
|
adata = g_new0 (FileAccessData, 1);
|
|
adata->path = g_strdup (path);
|
|
adata->atime = sb.st_atime;
|
|
adata->func = notify_func;
|
|
adata->udata = data;
|
|
if (mask == HAL_FILE_MONITOR_EVENT_ACCESS)
|
|
{
|
|
/* We will close the file descriptor when we release the adata. */
|
|
adata->owner = TRUE;
|
|
}
|
|
g_hash_table_insert (monitor->priv->fd_to_adata,
|
|
GINT_TO_POINTER (fd),
|
|
adata);
|
|
id = fd;
|
|
}
|
|
|
|
if ((mask & HAL_FILE_MONITOR_EVENT_CREATE) ||
|
|
(mask & HAL_FILE_MONITOR_EVENT_DELETE) ||
|
|
(mask & HAL_FILE_MONITOR_EVENT_CHANGE))
|
|
{
|
|
FileKqueueData *kdata;
|
|
int kmask;
|
|
gboolean isdir;
|
|
|
|
kmask = hal_mask_to_kmask (mask);
|
|
|
|
isdir = (sb.st_mode & S_IFDIR) != 0;
|
|
if (! isdir && mask == HAL_FILE_MONITOR_EVENT_CREATE)
|
|
{
|
|
/* We can't monitor creation on a file. */
|
|
goto done;
|
|
}
|
|
|
|
kdata = g_new0 (FileKqueueData, 1);
|
|
kdata->path = g_strdup (path);
|
|
kdata->omask = mask;
|
|
kdata->isdir = isdir;
|
|
kdata->func = notify_func;
|
|
kdata->udata = data;
|
|
if (isdir)
|
|
{
|
|
kdata->dir_contents = get_dir_contents (path);
|
|
}
|
|
|
|
/*g_warning ("XXX: Adding event with mask %s", fflags_to_str (kmask));*/
|
|
EV_SET (&ev, fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR,
|
|
kmask, 0, monitor);
|
|
if (kevent (monitor->priv->kqueue_fd, &ev, 1, NULL, 0, NULL) < 0)
|
|
{
|
|
monitor_release_kdata (monitor, fd, kdata);
|
|
goto done;
|
|
}
|
|
g_hash_table_insert (monitor->priv->fd_to_kdata,
|
|
GINT_TO_POINTER (fd),
|
|
kdata);
|
|
id = fd;
|
|
}
|
|
|
|
done:
|
|
return id;
|
|
}
|
|
|
|
static void
|
|
hal_file_monitor_remove_kdata (HalFileMonitor *monitor,
|
|
guint id)
|
|
{
|
|
FileKqueueData *kdata;
|
|
|
|
kdata = (FileKqueueData *) g_hash_table_lookup (monitor->priv->fd_to_kdata, GINT_TO_POINTER (id));
|
|
|
|
if (kdata)
|
|
{
|
|
g_hash_table_remove (monitor->priv->fd_to_kdata, GINT_TO_POINTER (id));
|
|
monitor_release_kdata (monitor, id, kdata);
|
|
}
|
|
}
|
|
|
|
static void
|
|
hal_file_monitor_remove_adata (HalFileMonitor *monitor,
|
|
guint id)
|
|
{
|
|
FileAccessData *adata;
|
|
|
|
adata = (FileAccessData *) g_hash_table_lookup (monitor->priv->fd_to_adata, GINT_TO_POINTER (id));
|
|
|
|
if (adata)
|
|
{
|
|
g_hash_table_remove (monitor->priv->fd_to_adata, GINT_TO_POINTER (id));
|
|
monitor_release_adata (monitor, id, adata);
|
|
}
|
|
}
|
|
|
|
void
|
|
hal_file_monitor_remove_notify (HalFileMonitor *monitor,
|
|
guint id)
|
|
{
|
|
if (! monitor->priv->initialized_kqueue)
|
|
return;
|
|
|
|
hal_file_monitor_remove_kdata (monitor, id);
|
|
hal_file_monitor_remove_adata (monitor, id);
|
|
|
|
}
|
|
|
|
static void
|
|
monitor_release_kdata (HalFileMonitor *monitor,
|
|
int fd,
|
|
FileKqueueData *data)
|
|
{
|
|
g_free (data->path);
|
|
data->path = NULL;
|
|
|
|
if (data->dir_contents)
|
|
{
|
|
g_hash_table_remove_all (data->dir_contents);
|
|
g_hash_table_destroy (data->dir_contents);
|
|
}
|
|
data->dir_contents = NULL;
|
|
|
|
close (fd);
|
|
|
|
g_free (data);
|
|
}
|
|
|
|
static void
|
|
monitor_release_adata (HalFileMonitor *monitor,
|
|
int fd,
|
|
FileAccessData *data)
|
|
{
|
|
g_free (data->path);
|
|
data->path = NULL;
|
|
|
|
if (data->owner)
|
|
{
|
|
close (fd);
|
|
}
|
|
|
|
g_free (data);
|
|
}
|
|
|
|
static gboolean
|
|
remove_kdata_foreach (gpointer fd,
|
|
FileKqueueData *data,
|
|
HalFileMonitor *monitor)
|
|
{
|
|
monitor_release_kdata (monitor, GPOINTER_TO_INT (fd), data);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
remove_adata_foreach (gpointer fd,
|
|
FileAccessData *data,
|
|
HalFileMonitor *monitor)
|
|
{
|
|
monitor_release_adata (monitor, GPOINTER_TO_INT (fd), data);
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
close_monitor (HalFileMonitor *monitor)
|
|
{
|
|
if (! monitor->priv->initialized_kqueue)
|
|
{
|
|
return;
|
|
}
|
|
|
|
monitor->priv->initialized_kqueue = FALSE;
|
|
|
|
g_hash_table_foreach_remove (monitor->priv->fd_to_kdata,
|
|
(GHRFunc) remove_kdata_foreach,
|
|
monitor);
|
|
|
|
g_hash_table_foreach_remove (monitor->priv->fd_to_adata,
|
|
(GHRFunc) remove_adata_foreach,
|
|
monitor);
|
|
|
|
if (monitor->priv->io_watch)
|
|
{
|
|
g_source_remove (monitor->priv->io_watch);
|
|
}
|
|
monitor->priv->io_watch = 0;
|
|
|
|
if (monitor->priv->access_source)
|
|
{
|
|
g_source_remove (monitor->priv->access_source);
|
|
}
|
|
monitor->priv->access_source = 0;
|
|
|
|
if (monitor->priv->kqueue_fd > -1)
|
|
{
|
|
close (monitor->priv->kqueue_fd);
|
|
}
|
|
monitor->priv->kqueue_fd = -1;
|
|
}
|
|
|
|
static gboolean
|
|
hal_file_access_monitor (gpointer data)
|
|
{
|
|
HalFileMonitor *monitor;
|
|
GList *keys, *l;
|
|
|
|
g_return_val_if_fail (HAL_IS_FILE_MONITOR (data), FALSE);
|
|
|
|
monitor = HAL_FILE_MONITOR (data);
|
|
|
|
g_return_val_if_fail (monitor->priv != NULL, FALSE);
|
|
|
|
keys = g_hash_table_get_keys (monitor->priv->fd_to_adata);
|
|
|
|
for (l = keys; l != NULL; l = l->next)
|
|
{
|
|
FileAccessData *adata;
|
|
struct stat sb;
|
|
int fd;
|
|
|
|
fd = GPOINTER_TO_INT (l->data);
|
|
adata = g_hash_table_lookup (monitor->priv->fd_to_adata,
|
|
l->data);
|
|
|
|
if (! adata)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (fstat (fd, &sb) == -1)
|
|
{
|
|
g_warning ("Failed to stat %s: %s", adata->path, g_strerror (errno));
|
|
hal_file_monitor_remove_adata (monitor, fd);
|
|
continue;
|
|
}
|
|
|
|
if (sb.st_atime != adata->atime)
|
|
{
|
|
adata->atime = sb.st_atime;
|
|
emit_monitor_event (monitor, HAL_FILE_MONITOR_EVENT_ACCESS, adata->path, adata->func, adata->udata);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
setup_monitor (HalFileMonitor *monitor)
|
|
{
|
|
GIOChannel *io_channel;
|
|
int fd;
|
|
|
|
if (monitor->priv->initialized_kqueue)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ((fd = kqueue ()) < 0)
|
|
{
|
|
g_warning ("Failed to initialize kqueue: %s",
|
|
g_strerror (errno));
|
|
return;
|
|
}
|
|
|
|
monitor->priv->kqueue_fd = fd;
|
|
|
|
monitor->priv->fd_to_kdata = g_hash_table_new (g_direct_hash,
|
|
g_direct_equal);
|
|
monitor->priv->fd_to_adata = g_hash_table_new (g_direct_hash,
|
|
g_direct_equal);
|
|
|
|
io_channel = g_io_channel_unix_new (fd);
|
|
monitor->priv->io_watch = g_io_add_watch (io_channel,
|
|
G_IO_IN|G_IO_PRI,
|
|
(GIOFunc) handle_kqueue_event,
|
|
monitor);
|
|
g_io_channel_unref (io_channel);
|
|
|
|
monitor->priv->access_source = g_timeout_add (1000, (GSourceFunc) hal_file_access_monitor, monitor);
|
|
|
|
monitor->priv->initialized_kqueue = TRUE;
|
|
}
|
|
|
|
static void
|
|
hal_file_monitor_init (HalFileMonitor *monitor)
|
|
{
|
|
monitor->priv = HAL_FILE_MONITOR_GET_PRIVATE (monitor);
|
|
|
|
setup_monitor (monitor);
|
|
}
|
|
|
|
static void
|
|
hal_file_monitor_class_init (HalFileMonitorClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->finalize = hal_file_monitor_finalize;
|
|
|
|
g_type_class_add_private (klass, sizeof (HalFileMonitorPrivate));
|
|
}
|
|
|
|
static void
|
|
hal_file_monitor_finalize (GObject *object)
|
|
{
|
|
HalFileMonitor *monitor;
|
|
|
|
g_return_if_fail (object != NULL);
|
|
g_return_if_fail (HAL_IS_FILE_MONITOR (object));
|
|
|
|
monitor = HAL_FILE_MONITOR (object);
|
|
|
|
g_return_if_fail (monitor->priv != NULL);
|
|
|
|
close_monitor (monitor);
|
|
|
|
g_hash_table_destroy (monitor->priv->fd_to_kdata);
|
|
g_hash_table_destroy (monitor->priv->fd_to_adata);
|
|
|
|
G_OBJECT_CLASS (hal_file_monitor_parent_class)->finalize (object);
|
|
}
|
|
|
|
HalFileMonitor *
|
|
hal_file_monitor_new (void)
|
|
{
|
|
if (monitor_object != NULL)
|
|
{
|
|
g_object_ref (monitor_object);
|
|
}
|
|
else
|
|
{
|
|
monitor_object = g_object_new (HAL_TYPE_FILE_MONITOR, NULL);
|
|
|
|
g_object_add_weak_pointer (monitor_object,
|
|
(gpointer *) &monitor_object);
|
|
}
|
|
|
|
return HAL_FILE_MONITOR (monitor_object);
|
|
}
|
|
|
|
/*
|
|
static char *
|
|
fflags_to_str (int fflags)
|
|
{
|
|
|
|
GString *out;
|
|
|
|
out = g_string_new (NULL);
|
|
|
|
if (fflags & NOTE_WRITE)
|
|
{
|
|
g_string_append (out, " WRITE ");
|
|
}
|
|
if (fflags & NOTE_EXTEND)
|
|
{
|
|
g_string_append (out, " EXTEND ");
|
|
}
|
|
if (fflags & NOTE_ATTRIB)
|
|
{
|
|
g_string_append (out, " ATTRIB ");
|
|
}
|
|
if (fflags & NOTE_LINK)
|
|
{
|
|
g_string_append (out, " LINK ");
|
|
}
|
|
if (fflags & NOTE_DELETE)
|
|
{
|
|
g_string_append (out, " DELETE ");
|
|
}
|
|
if (fflags & NOTE_REVOKE)
|
|
{
|
|
g_string_append (out, " REVOKE ");
|
|
}
|
|
if (fflags & NOTE_RENAME)
|
|
{
|
|
g_string_append (out, " RENAME ");
|
|
}
|
|
|
|
return g_string_free (out, FALSE);
|
|
}
|
|
*/
|
|
|
|
static void
|
|
emit_monitor_event (HalFileMonitor *monitor,
|
|
HalFileMonitorEvent event,
|
|
const char *path,
|
|
HalFileMonitorNotifyFunc func,
|
|
gpointer udata)
|
|
{
|
|
if (func)
|
|
{
|
|
func (monitor, event, path, udata);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
handle_kqueue_event (GIOChannel *source,
|
|
GIOCondition condition,
|
|
gpointer udata)
|
|
{
|
|
struct kevent ev;
|
|
struct timespec timeout = { 0, 0 };
|
|
int nevents;
|
|
HalFileMonitor *monitor;
|
|
|
|
g_return_val_if_fail (HAL_IS_FILE_MONITOR (udata), FALSE);
|
|
|
|
monitor = HAL_FILE_MONITOR (udata);
|
|
|
|
g_return_val_if_fail (monitor->priv != NULL, FALSE);
|
|
|
|
g_return_val_if_fail (monitor->priv->kqueue_fd > -1, FALSE);
|
|
|
|
nevents = kevent (monitor->priv->kqueue_fd, NULL, 0, &ev, 1, &timeout);
|
|
if (nevents == 1)
|
|
{
|
|
int fd;
|
|
FileKqueueData *data;
|
|
|
|
fd = ev.ident;
|
|
data = g_hash_table_lookup (monitor->priv->fd_to_kdata,
|
|
GINT_TO_POINTER (fd));
|
|
if (! data)
|
|
{
|
|
/* The monitor may have been deleted. */
|
|
return TRUE;
|
|
}
|
|
|
|
if ((data->omask & HAL_FILE_MONITOR_EVENT_DELETE) &&
|
|
(ev.fflags & VN_NOTE_DELETED))
|
|
{
|
|
emit_monitor_event (monitor, HAL_FILE_MONITOR_EVENT_DELETE, data->path, data->func, data->udata);
|
|
hal_file_monitor_remove_kdata (monitor, fd);
|
|
return TRUE;
|
|
}
|
|
|
|
if ((data->omask & HAL_FILE_MONITOR_EVENT_CREATE) ||
|
|
(data->omask & HAL_FILE_MONITOR_EVENT_DELETE))
|
|
{
|
|
if (data->isdir)
|
|
{
|
|
GSList *added = NULL;
|
|
GSList *removed = NULL;
|
|
GSList *l;
|
|
GHashTable *table;
|
|
gboolean found_change = FALSE;
|
|
|
|
table = diff_dir_contents (data, &added, &removed);
|
|
if (data->omask & HAL_FILE_MONITOR_EVENT_CREATE)
|
|
{
|
|
for (l = added; l != NULL; l = l->next)
|
|
{
|
|
char *path;
|
|
|
|
path = g_build_filename (data->path, l->data, NULL);
|
|
emit_monitor_event (monitor, HAL_FILE_MONITOR_EVENT_CREATE, path, data->func, data->udata);
|
|
g_free (path);
|
|
}
|
|
}
|
|
|
|
if (data->omask & HAL_FILE_MONITOR_EVENT_DELETE)
|
|
{
|
|
for (l = removed; l != NULL; l = l->next)
|
|
{
|
|
char *path;
|
|
|
|
path = g_build_filename (data->path, l->data, NULL);
|
|
emit_monitor_event (monitor, HAL_FILE_MONITOR_EVENT_DELETE, path, data->func, data->udata);
|
|
g_free (path);
|
|
}
|
|
}
|
|
|
|
if (added || removed)
|
|
{
|
|
g_hash_table_remove_all (data->dir_contents);
|
|
g_hash_table_destroy (data->dir_contents);
|
|
data->dir_contents = table;
|
|
found_change = TRUE;
|
|
}
|
|
else if (table)
|
|
{
|
|
g_hash_table_remove_all (table);
|
|
g_hash_table_destroy (table);
|
|
}
|
|
else
|
|
{
|
|
hal_file_monitor_remove_kdata (monitor, fd);
|
|
}
|
|
|
|
if (added)
|
|
{
|
|
g_slist_foreach (added, (GFunc) g_free, NULL);
|
|
g_slist_free (added);
|
|
}
|
|
if (removed)
|
|
{
|
|
g_slist_foreach (removed, (GFunc) g_free, NULL);
|
|
g_slist_free (removed);
|
|
}
|
|
|
|
if (found_change)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (data->omask & HAL_FILE_MONITOR_EVENT_CHANGE)
|
|
{
|
|
emit_monitor_event (monitor, HAL_FILE_MONITOR_EVENT_CHANGE, data->path, data->func, data->udata);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_warning ("Failed to read from kqueue: %s", g_strerror (errno));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static GHashTable *
|
|
diff_dir_contents (FileKqueueData *data,
|
|
GSList **added,
|
|
GSList **removed)
|
|
{
|
|
GDir *dir;
|
|
GError *err = NULL;
|
|
GHashTable *table = NULL;
|
|
|
|
dir = g_dir_open (data->path, 0, &err);
|
|
if (dir)
|
|
{
|
|
const char *fname;
|
|
|
|
table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
|
|
|
|
while ((fname = g_dir_read_name (dir)))
|
|
{
|
|
if (added)
|
|
{
|
|
if (! g_hash_table_lookup (data->dir_contents, fname))
|
|
{
|
|
*added = g_slist_prepend (*added, g_strdup (fname));
|
|
}
|
|
}
|
|
|
|
g_hash_table_insert (table, g_strdup (fname), GINT_TO_POINTER (TRUE));
|
|
}
|
|
|
|
g_dir_close (dir);
|
|
|
|
if (removed)
|
|
{
|
|
GList *keys;
|
|
GList *l;
|
|
|
|
keys = g_hash_table_get_keys (data->dir_contents);
|
|
|
|
for (l = keys; l != NULL; l = l->next)
|
|
{
|
|
if (! g_hash_table_lookup (table, l->data))
|
|
{
|
|
*removed = g_slist_prepend (*removed, g_strdup (l->data));
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
g_warning ("Failed to open directory: %s", err->message);
|
|
g_error_free (err);
|
|
}
|
|
|
|
return table;
|
|
}
|
|
|
|
static GHashTable *
|
|
get_dir_contents (const char *path)
|
|
{
|
|
GDir *dir;
|
|
GError *err = NULL;
|
|
GHashTable *table = NULL;
|
|
|
|
dir = g_dir_open (path, 0, &err);
|
|
if (dir)
|
|
{
|
|
const char *fname;
|
|
|
|
table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
|
|
|
|
while ((fname = g_dir_read_name (dir)))
|
|
{
|
|
g_hash_table_insert (table, g_strdup (fname), GINT_TO_POINTER (TRUE));
|
|
}
|
|
g_dir_close (dir);
|
|
}
|
|
else
|
|
{
|
|
g_warning ("Failed to open directory %s: %s\n", path, err->message);
|
|
g_error_free (err);
|
|
}
|
|
|
|
return table;
|
|
}
|
|
|
|
/*
|
|
static void
|
|
print_event (HalFileMonitor *monitor,
|
|
HalFileMonitorEvent event,
|
|
const char *path,
|
|
gpointer udata)
|
|
{
|
|
GString *ename;
|
|
|
|
ename = g_string_new (NULL);
|
|
|
|
if (event & HAL_FILE_MONITOR_EVENT_ACCESS)
|
|
{
|
|
g_string_append (ename, "ACCESS ");
|
|
}
|
|
if (event & HAL_FILE_MONITOR_EVENT_CREATE)
|
|
{
|
|
g_string_append (ename, "CREATE ");
|
|
}
|
|
if (event & HAL_FILE_MONITOR_EVENT_DELETE)
|
|
{
|
|
g_string_append (ename, "DELETE ");
|
|
}
|
|
if (event & HAL_FILE_MONITOR_EVENT_CHANGE)
|
|
{
|
|
g_string_append (ename, "CHANGE ");
|
|
}
|
|
|
|
printf("Received event for %s: %s\n", path, g_string_free (ename, FALSE));
|
|
}
|
|
|
|
int
|
|
main(void)
|
|
{
|
|
const char *path = "/tmp/kqueue.d";
|
|
guint watch;
|
|
GMainLoop *loop;
|
|
HalFileMonitor *monitor;
|
|
|
|
g_type_init ();
|
|
|
|
monitor = hal_file_monitor_new ();
|
|
hal_file_monitor_add_notify (monitor, path, HAL_FILE_MONITOR_EVENT_CREATE|HAL_FILE_MONITOR_EVENT_DELETE|HAL_FILE_MONITOR_EVENT_CHANGE,print_event, NULL);
|
|
|
|
loop = g_main_loop_new (NULL, FALSE);
|
|
|
|
g_main_loop_run (loop);
|
|
|
|
return 0;
|
|
}
|
|
*/
|