[tracker-miners/wip/carlosg/fanotify: 2/3] libtracker-miner: Split TrackerMonitor abstract class and implementation
- From: Carlos Garnacho <carlosg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [tracker-miners/wip/carlosg/fanotify: 2/3] libtracker-miner: Split TrackerMonitor abstract class and implementation
- Date: Wed, 29 Dec 2021 15:57:47 +0000 (UTC)
commit 8221aa7edc3a5204500fe8b6f618e9ac00fee000
Author: Carlos Garnacho <carlosg gnome org>
Date: Wed Dec 29 14:22:27 2021 +0100
libtracker-miner: Split TrackerMonitor abstract class and implementation
Since we want multiple TrackerMonitor implementations around, split
it into a base abstract TrackerMonitor class, and a TrackerMonitorGlib
subclass that has the GFileMonitor implementation.
src/libtracker-miner/meson.build | 1 +
src/libtracker-miner/tracker-file-notifier.c | 6 +-
src/libtracker-miner/tracker-monitor-glib.c | 1322 +++++++++++++++++++++++
src/libtracker-miner/tracker-monitor-glib.h | 43 +
src/libtracker-miner/tracker-monitor.c | 1381 +++----------------------
src/libtracker-miner/tracker-monitor.h | 40 +-
tests/libtracker-miner/tracker-monitor-test.c | 6 +-
7 files changed, 1563 insertions(+), 1236 deletions(-)
---
diff --git a/src/libtracker-miner/meson.build b/src/libtracker-miner/meson.build
index 68276d24d..9436cbd89 100644
--- a/src/libtracker-miner/meson.build
+++ b/src/libtracker-miner/meson.build
@@ -11,6 +11,7 @@ private_sources = [
'tracker-file-notifier.c',
'tracker-lru.c',
'tracker-monitor.c',
+ 'tracker-monitor-glib.c',
'tracker-priority-queue.c',
'tracker-task-pool.c',
'tracker-sparql-buffer.c',
diff --git a/src/libtracker-miner/tracker-file-notifier.c b/src/libtracker-miner/tracker-file-notifier.c
index 8422c7f4d..a6cfbab66 100644
--- a/src/libtracker-miner/tracker-file-notifier.c
+++ b/src/libtracker-miner/tracker-file-notifier.c
@@ -27,7 +27,7 @@
#include "tracker-file-notifier.h"
#include "tracker-crawler.h"
-#include "tracker-monitor.h"
+#include "tracker-monitor-glib.h"
enum {
PROP_0,
@@ -1682,9 +1682,9 @@ tracker_file_notifier_init (TrackerFileNotifier *notifier)
priv->stopped = TRUE;
/* Set up monitor */
- priv->monitor = tracker_monitor_new ();
+ priv->monitor = tracker_monitor_new (&error);
- if (!g_initable_init (G_INITABLE (priv->monitor), NULL, &error)) {
+ if (!priv->monitor) {
g_warning ("Could not init monitor: %s", error->message);
g_error_free (error);
} else {
diff --git a/src/libtracker-miner/tracker-monitor-glib.c b/src/libtracker-miner/tracker-monitor-glib.c
new file mode 100644
index 000000000..f702ce2a9
--- /dev/null
+++ b/src/libtracker-miner/tracker-monitor-glib.c
@@ -0,0 +1,1322 @@
+/*
+ * Copyright (C) 2009, Nokia <ivan frade nokia com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config-miners.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <gio/gio.h>
+
+#if defined (__OpenBSD__) || defined (__FreeBSD__) || defined (__NetBSD__) || defined (__APPLE__)
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#define TRACKER_MONITOR_KQUEUE
+#endif
+
+#include "tracker-monitor-glib.h"
+#include "tracker-monitor-private.h"
+
+#include "libtracker-miners-common/tracker-debug.h"
+
+typedef struct TrackerMonitorGlibPrivate TrackerMonitorGlibPrivate;
+
+struct TrackerMonitorGlibPrivate {
+ GHashTable *monitored_dirs;
+
+ gboolean enabled;
+
+ guint monitor_limit;
+ gboolean monitor_limit_warned;
+ guint monitors_ignored;
+
+ /* For FAM, the _CHANGES_DONE event is not signalled, so we
+ * have to just use the _CHANGED event instead.
+ */
+ gboolean use_changed_event;
+
+ struct {
+ GMainContext *owner_context;
+ GMainContext *monitor_context;
+ GMainLoop *monitor_thread_loop;
+ GThread *monitor_thread;
+ GHashTable *cached_events;
+ GHashTable *monitors;
+ GMutex mutex;
+ GCond cond;
+ gint n_requests;
+ } thread;
+};
+
+struct _TrackerMonitorGlib {
+ TrackerMonitor parent_instance;
+};
+
+typedef struct {
+ GFile *file;
+ gchar *file_uri;
+ GFile *other_file;
+ gchar *other_file_uri;
+ gboolean is_directory;
+ guint32 event_type;
+ gboolean expirable;
+} EventData;
+
+typedef struct {
+ TrackerMonitorGlib *monitor;
+ GFile *file;
+ GFile *other_file;
+ GSource *source;
+ gboolean is_directory;
+ GFileMonitorEvent event_type;
+} MonitorEvent;
+
+typedef enum {
+ MONITOR_REQUEST_ADD,
+ MONITOR_REQUEST_REMOVE,
+} MonitorRequestType;
+
+typedef struct {
+ TrackerMonitorGlib *monitor;
+ MonitorRequestType type;
+ GList *files;
+} MonitorRequest;
+
+enum {
+ ITEM_CREATED,
+ ITEM_UPDATED,
+ ITEM_ATTRIBUTE_UPDATED,
+ ITEM_DELETED,
+ ITEM_MOVED,
+ LAST_SIGNAL
+};
+
+enum {
+ PROP_0,
+ PROP_ENABLED,
+ PROP_LIMIT,
+ PROP_COUNT,
+ PROP_IGNORED,
+};
+
+static void tracker_monitor_glib_finalize (GObject *object);
+static void tracker_monitor_glib_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void tracker_monitor_glib_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static guint get_kqueue_limit (void);
+static guint get_inotify_limit (void);
+static GFileMonitor * directory_monitor_new (TrackerMonitorGlib *monitor,
+ GFile *file);
+static void directory_monitor_cancel (GFileMonitor *dir_monitor);
+
+
+static gboolean monitor_cancel_recursively (TrackerMonitorGlib *monitor,
+ GFile *file);
+
+static void tracker_monitor_glib_initable_iface_init (GInitableIface *iface);
+
+static gboolean tracker_monitor_glib_add (TrackerMonitor *monitor,
+ GFile *file);
+static gboolean tracker_monitor_glib_remove (TrackerMonitor *monitor,
+ GFile *file);
+static gboolean tracker_monitor_glib_remove_recursively (TrackerMonitor *monitor,
+ GFile *file,
+ gboolean only_children);
+static gboolean tracker_monitor_glib_move (TrackerMonitor *monitor,
+ GFile *file,
+ GFile *other_file);
+static gboolean tracker_monitor_glib_is_watched (TrackerMonitor *monitor,
+ GFile *file);
+static void tracker_monitor_glib_set_enabled (TrackerMonitorGlib *monitor,
+ gboolean enabled);
+
+G_DEFINE_TYPE_WITH_CODE (TrackerMonitorGlib, tracker_monitor_glib, TRACKER_TYPE_MONITOR,
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+ tracker_monitor_glib_initable_iface_init)
+ G_ADD_PRIVATE (TrackerMonitorGlib))
+
+static gpointer
+monitor_thread_func (gpointer user_data)
+{
+ TrackerMonitorGlib *monitor = user_data;
+ TrackerMonitorGlibPrivate *priv;
+
+ priv = tracker_monitor_glib_get_instance_private (monitor);
+ g_main_context_push_thread_default (priv->thread.monitor_context);
+ g_main_loop_run (priv->thread.monitor_thread_loop);
+ g_main_context_pop_thread_default (priv->thread.monitor_context);
+ g_main_loop_unref (priv->thread.monitor_thread_loop);
+
+ return NULL;
+}
+
+static gboolean
+tracker_monitor_glib_initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GType monitor_backend;
+ const gchar *name;
+ GError *inner_error = NULL;
+ TrackerMonitorGlibPrivate *priv;
+ GFile *file;
+ GFileMonitor *monitor;
+
+ priv = tracker_monitor_glib_get_instance_private (TRACKER_MONITOR_GLIB (initable));
+
+ /* For the first monitor we get the type and find out if we
+ * are using inotify, FAM, polling, etc.
+ */
+ file = g_file_new_for_path (g_get_home_dir ());
+ monitor = g_file_monitor_directory (file,
+ G_FILE_MONITOR_WATCH_MOVES,
+ NULL,
+ &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ monitor_backend = G_OBJECT_TYPE (monitor);
+
+ /* We use the name because the type itself is actually
+ * private and not available publically. Note this is
+ * subject to change, but unlikely of course.
+ */
+ name = g_type_name (monitor_backend);
+
+ /* Set limits based on backend... */
+ if (strcmp (name, "GInotifyDirectoryMonitor") == 0 ||
+ strcmp (name, "GInotifyFileMonitor") == 0) {
+ /* Using inotify */
+ TRACKER_NOTE (MONITORS, g_message ("Monitor backend is Inotify"));
+
+ /* Setting limit based on kernel
+ * settings in /proc...
+ */
+ priv->monitor_limit = get_inotify_limit ();
+
+ /* We don't use 100% of the monitors, we allow other
+ * applications to have at least 500 or so to use
+ * between them selves. This only
+ * applies to inotify because it is a
+ * user shared resource.
+ */
+ priv->monitor_limit -= 500;
+
+ /* Make sure we don't end up with a
+ * negative maximum.
+ */
+ priv->monitor_limit = MAX (priv->monitor_limit, 0);
+ } else if (strcmp (name, "GKqueueDirectoryMonitor") == 0 ||
+ strcmp (name, "GKqueueFileMonitor") == 0) {
+ /* Using kqueue(2) */
+ TRACKER_NOTE (MONITORS, g_message ("Monitor backend is kqueue"));
+
+ priv->monitor_limit = get_kqueue_limit ();
+ } else if (strcmp (name, "GFamDirectoryMonitor") == 0) {
+ /* Using Fam */
+ TRACKER_NOTE (MONITORS, g_message ("Monitor backend is Fam"));
+
+ /* Setting limit to an arbitary limit
+ * based on testing
+ */
+ priv->monitor_limit = 400;
+ priv->use_changed_event = TRUE;
+ } else if (strcmp (name, "GWin32DirectoryMonitor") == 0) {
+ /* Using Windows */
+ TRACKER_NOTE (MONITORS, g_message ("Monitor backend is Windows"));
+
+ /* Guessing limit... */
+ priv->monitor_limit = 8192;
+ } else {
+ /* Unknown */
+ g_warning ("Monitor backend:'%s' is unhandled. Monitoring will be disabled",
+ name);
+ priv->enabled = FALSE;
+ }
+
+ if (priv->enabled)
+ TRACKER_NOTE (MONITORS, g_message ("Monitor limit is %d", priv->monitor_limit));
+
+ g_file_monitor_cancel (monitor);
+ g_object_unref (monitor);
+ g_object_unref (file);
+
+ priv->thread.owner_context = g_main_context_ref_thread_default ();
+ priv->thread.monitor_context = g_main_context_new ();
+ priv->thread.monitor_thread_loop = g_main_loop_new (priv->thread.monitor_context, FALSE);
+
+ priv->thread.monitor_thread =
+ g_thread_try_new ("Monitor thread",
+ monitor_thread_func,
+ initable,
+ &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+tracker_monitor_glib_initable_iface_init (GInitableIface *iface)
+{
+ iface->init = tracker_monitor_glib_initable_init;
+}
+
+static void
+tracker_monitor_glib_class_init (TrackerMonitorGlibClass *klass)
+{
+ TrackerMonitorClass *monitor_class;
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ monitor_class = TRACKER_MONITOR_CLASS (klass);
+
+ object_class->finalize = tracker_monitor_glib_finalize;
+ object_class->set_property = tracker_monitor_glib_set_property;
+ object_class->get_property = tracker_monitor_glib_get_property;
+
+ monitor_class->add = tracker_monitor_glib_add;
+ monitor_class->remove = tracker_monitor_glib_remove;
+ monitor_class->remove_recursively = tracker_monitor_glib_remove_recursively;
+ monitor_class->move = tracker_monitor_glib_move;
+ monitor_class->is_watched = tracker_monitor_glib_is_watched;
+
+ g_object_class_override_property (object_class, PROP_ENABLED, "enabled");
+ g_object_class_override_property (object_class, PROP_LIMIT, "limit");
+ g_object_class_override_property (object_class, PROP_COUNT, "count");
+ g_object_class_override_property (object_class, PROP_IGNORED, "ignored");
+}
+
+static MonitorEvent *
+monitor_event_new (TrackerMonitorGlib *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ gboolean is_directory)
+{
+ MonitorEvent *event;
+
+ event = g_new0 (MonitorEvent, 1);
+ event->monitor = g_object_ref (monitor);
+ event->file = g_object_ref (file);
+ g_set_object (&event->other_file, other_file);
+ event->event_type = event_type;
+ event->is_directory = is_directory;
+
+ return event;
+}
+
+static void
+monitor_event_free (MonitorEvent *event)
+{
+ g_object_unref (event->monitor);
+ g_object_unref (event->file);
+ g_clear_object (&event->other_file);
+ g_clear_pointer (&event->source, g_source_destroy);
+ g_free (event);
+}
+
+static void
+tracker_monitor_glib_init (TrackerMonitorGlib *object)
+{
+ TrackerMonitorGlibPrivate *priv;
+
+ priv = tracker_monitor_glib_get_instance_private (object);
+
+ /* By default we enable monitoring */
+ priv->enabled = TRUE;
+
+ /* Create monitors table for this module */
+ priv->monitored_dirs =
+ g_hash_table_new_full (g_file_hash,
+ (GEqualFunc) g_file_equal,
+ (GDestroyNotify) g_object_unref,
+ NULL);
+
+ priv->thread.cached_events =
+ g_hash_table_new_full (g_file_hash,
+ (GEqualFunc) g_file_equal,
+ g_object_unref,
+ (GDestroyNotify) monitor_event_free);
+
+ priv->thread.monitors =
+ g_hash_table_new_full (g_file_hash,
+ (GEqualFunc) g_file_equal,
+ (GDestroyNotify) g_object_unref,
+ (GDestroyNotify) directory_monitor_cancel);
+
+ g_mutex_init (&priv->thread.mutex);
+ g_cond_init (&priv->thread.cond);
+}
+
+static gboolean
+quit_thread (TrackerMonitor *monitor)
+{
+ TrackerMonitorGlibPrivate *priv;
+
+ priv = tracker_monitor_glib_get_instance_private (TRACKER_MONITOR_GLIB (monitor));
+ g_main_loop_quit (priv->thread.monitor_thread_loop);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+tracker_monitor_glib_finalize (GObject *object)
+{
+ TrackerMonitorGlibPrivate *priv;
+
+ priv = tracker_monitor_glib_get_instance_private (TRACKER_MONITOR_GLIB (object));
+
+ if (priv->thread.monitor_thread_loop) {
+ g_main_context_invoke_full (priv->thread.monitor_context,
+ G_PRIORITY_HIGH,
+ (GSourceFunc) quit_thread,
+ object, NULL);
+ }
+
+ if (priv->thread.monitor_thread)
+ g_thread_join (priv->thread.monitor_thread);
+
+ g_clear_pointer (&priv->thread.monitor_context, g_main_context_unref);
+ g_clear_pointer (&priv->thread.owner_context, g_main_context_unref);
+ g_clear_pointer (&priv->thread.cached_events, g_hash_table_unref);
+ g_clear_pointer (&priv->thread.monitors, g_hash_table_unref);
+
+ g_hash_table_unref (priv->monitored_dirs);
+
+ G_OBJECT_CLASS (tracker_monitor_glib_parent_class)->finalize (object);
+}
+
+static void
+tracker_monitor_glib_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ case PROP_ENABLED:
+ tracker_monitor_glib_set_enabled (TRACKER_MONITOR_GLIB (object),
+ g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+tracker_monitor_glib_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ TrackerMonitorGlibPrivate *priv;
+
+ priv = tracker_monitor_glib_get_instance_private (TRACKER_MONITOR_GLIB (object));
+
+ switch (prop_id) {
+ case PROP_ENABLED:
+ g_value_set_boolean (value, priv->enabled);
+ break;
+ case PROP_LIMIT:
+ g_value_set_uint (value, priv->monitor_limit);
+ break;
+ case PROP_COUNT:
+ g_value_set_uint (value, g_hash_table_size (priv->monitored_dirs));
+ break;
+ case PROP_IGNORED:
+ g_value_set_uint (value, priv->monitors_ignored);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static guint
+get_kqueue_limit (void)
+{
+ guint limit = 400;
+
+#ifdef TRACKER_MONITOR_KQUEUE
+ struct rlimit rl;
+ if (getrlimit (RLIMIT_NOFILE, &rl) == 0) {
+ rl.rlim_cur = rl.rlim_max;
+ } else {
+ return limit;
+ }
+
+ if (setrlimit(RLIMIT_NOFILE, &rl) == 0)
+ limit = (rl.rlim_cur * 90) / 100;
+#endif /* TRACKER_MONITOR_KQUEUE */
+
+ return limit;
+}
+
+static guint
+get_inotify_limit (void)
+{
+ GError *error = NULL;
+ const gchar *filename;
+ gchar *contents = NULL;
+ guint limit;
+
+ filename = "/proc/sys/fs/inotify/max_user_watches";
+
+ if (!g_file_get_contents (filename,
+ &contents,
+ NULL,
+ &error)) {
+ g_warning ("Couldn't get INotify monitor limit from:'%s', %s",
+ filename,
+ error ? error->message : "no error given");
+ g_clear_error (&error);
+
+ /* Setting limit to an arbitary limit */
+ limit = 8192;
+ } else {
+ limit = atoi (contents);
+ g_free (contents);
+ }
+
+ return limit;
+}
+
+static gboolean
+check_is_directory (TrackerMonitorGlib *monitor,
+ GFile *file)
+{
+ GFileType file_type;
+
+ file_type = g_file_query_file_type (file, G_FILE_QUERY_INFO_NONE, NULL);
+
+ if (file_type == G_FILE_TYPE_DIRECTORY)
+ return TRUE;
+
+ if (file_type == G_FILE_TYPE_UNKNOWN) {
+ TrackerMonitorGlibPrivate *priv;
+
+ priv = tracker_monitor_glib_get_instance_private (monitor);
+
+ /* Whatever it was, it's gone. Check the monitors
+ * hashtable to know whether it was a directory
+ * we knew about
+ */
+ if (g_hash_table_lookup (priv->thread.monitors, file) != NULL)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* Executed in monitor thread */
+static gboolean
+monitor_request_execute (MonitorRequest *request)
+{
+ TrackerMonitorGlibPrivate *priv;
+
+ priv = tracker_monitor_glib_get_instance_private (request->monitor);
+
+ g_mutex_lock (&priv->thread.mutex);
+
+ while (request->files) {
+ GFile *file = request->files->data;
+
+ if (request->type == MONITOR_REQUEST_ADD) {
+ GFileMonitor *monitor;
+
+ monitor = directory_monitor_new (request->monitor,
+ file);
+ if (monitor) {
+ g_hash_table_insert (priv->thread.monitors,
+ g_object_ref (file),
+ monitor);
+ }
+ } else if (request->type == MONITOR_REQUEST_REMOVE) {
+ g_hash_table_remove (priv->thread.monitors,
+ file);
+ } else {
+ g_assert_not_reached ();
+ }
+
+ request->files = g_list_remove (request->files, file);
+ g_object_unref (file);
+ }
+
+ if (g_atomic_int_dec_and_test (&priv->thread.n_requests))
+ g_cond_signal (&priv->thread.cond);
+
+ g_mutex_unlock (&priv->thread.mutex);
+
+ return G_SOURCE_REMOVE;
+}
+
+/* Executed in main thread */
+static void
+monitor_request_queue (TrackerMonitorGlib *monitor,
+ MonitorRequest *request)
+{
+ TrackerMonitorGlibPrivate *priv;
+
+ priv = tracker_monitor_glib_get_instance_private (monitor);
+
+ g_atomic_int_inc (&priv->thread.n_requests);
+ g_main_context_invoke_full (priv->thread.monitor_context,
+ G_PRIORITY_DEFAULT,
+ (GSourceFunc) monitor_request_execute,
+ request, g_free);
+}
+
+static void
+block_for_requests (TrackerMonitorGlib *monitor)
+{
+ TrackerMonitorGlibPrivate *priv;
+
+ priv = tracker_monitor_glib_get_instance_private (monitor);
+
+ g_mutex_lock (&priv->thread.mutex);
+
+ while (g_atomic_int_get (&priv->thread.n_requests) != 0)
+ g_cond_wait (&priv->thread.cond, &priv->thread.mutex);
+
+ g_mutex_unlock (&priv->thread.mutex);
+}
+
+static gboolean
+tracker_monitor_glib_move (TrackerMonitor *monitor,
+ GFile *old_file,
+ GFile *new_file)
+{
+ TrackerMonitorGlibPrivate *priv;
+ GHashTableIter iter;
+ MonitorRequest *request;
+ gchar *old_prefix;
+ gpointer iter_file;
+ guint items_moved = 0;
+
+ priv = tracker_monitor_glib_get_instance_private (TRACKER_MONITOR_GLIB (monitor));
+
+ /* So this is tricky. What we have to do is:
+ *
+ * 1) Add all monitors for the new_file directory hierarchy
+ * 2) Then remove the monitors for old_file
+ *
+ * This order is necessary because inotify can reuse watch
+ * descriptors, and libinotify will remove handles
+ * asynchronously on IN_IGNORE, so the opposite sequence
+ * may possibly remove valid, just added, monitors.
+ */
+ request = g_new0 (MonitorRequest, 1);
+ request->monitor = TRACKER_MONITOR_GLIB (monitor);
+ request->type = MONITOR_REQUEST_ADD;
+
+ old_prefix = g_file_get_path (old_file);
+
+ /* Find out which subdirectories should have a file monitor added */
+ g_hash_table_iter_init (&iter, priv->monitored_dirs);
+ while (g_hash_table_iter_next (&iter, &iter_file, NULL)) {
+ GFile *f;
+ gchar *old_path, *new_path;
+ gchar *new_prefix;
+ gchar *p;
+
+ if (!g_file_has_prefix (iter_file, old_file) &&
+ !g_file_equal (iter_file, old_file)) {
+ continue;
+ }
+
+ old_path = g_file_get_path (iter_file);
+ p = strstr (old_path, old_prefix);
+
+ if (!p || strcmp (p, old_prefix) == 0) {
+ g_free (old_path);
+ continue;
+ }
+
+ /* Move to end of prefix */
+ p += strlen (old_prefix) + 1;
+
+ /* Check this is not the end of the string */
+ if (*p == '\0') {
+ g_free (old_path);
+ continue;
+ }
+
+ new_prefix = g_file_get_path (new_file);
+ new_path = g_build_path (G_DIR_SEPARATOR_S, new_prefix, p, NULL);
+ g_free (new_prefix);
+
+ f = g_file_new_for_path (new_path);
+ g_free (new_path);
+
+ request->files = g_list_prepend (request->files, g_object_ref (f));
+
+ g_object_unref (f);
+ g_free (old_path);
+ items_moved++;
+ }
+
+ /* Add a new monitor for the top level directory */
+ tracker_monitor_glib_add (monitor, new_file);
+
+ /* Add new monitors for all subdirectories */
+ monitor_request_queue (TRACKER_MONITOR_GLIB (monitor), request);
+
+ /* Remove the monitor for the old top level directory hierarchy */
+ tracker_monitor_glib_remove_recursively (monitor, old_file, FALSE);
+
+ g_free (old_prefix);
+
+ block_for_requests (TRACKER_MONITOR_GLIB (monitor));
+
+ return items_moved > 0;
+}
+
+static const gchar *
+monitor_event_to_string (GFileMonitorEvent event_type)
+{
+ switch (event_type) {
+ case G_FILE_MONITOR_EVENT_CHANGED:
+ return "G_FILE_MONITOR_EVENT_CHANGED";
+ case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
+ return "G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT";
+ case G_FILE_MONITOR_EVENT_DELETED:
+ return "G_FILE_MONITOR_EVENT_DELETED";
+ case G_FILE_MONITOR_EVENT_CREATED:
+ return "G_FILE_MONITOR_EVENT_CREATED";
+ case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
+ return "G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED";
+ case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
+ return "G_FILE_MONITOR_EVENT_PRE_UNMOUNT";
+ case G_FILE_MONITOR_EVENT_UNMOUNTED:
+ return "G_FILE_MONITOR_EVENT_UNMOUNTED";
+ case G_FILE_MONITOR_EVENT_MOVED:
+ return "G_FILE_MONITOR_EVENT_MOVED";
+ case G_FILE_MONITOR_EVENT_RENAMED:
+ return "G_FILE_MONITOR_EVENT_RENAMED";
+ case G_FILE_MONITOR_EVENT_MOVED_IN:
+ return "G_FILE_MONITOR_EVENT_MOVED_IN";
+ case G_FILE_MONITOR_EVENT_MOVED_OUT:
+ return "G_FILE_MONITOR_EVENT_MOVED_OUT";
+ break;
+ }
+
+ return "unknown";
+}
+
+/* Executed in main thread */
+static gboolean
+emit_signal_for_event (MonitorEvent *event)
+{
+ TrackerMonitor *monitor = TRACKER_MONITOR (event->monitor);
+ gboolean is_directory = event->is_directory;
+ GFile *file = event->file;
+ GFile *other_file = event->other_file;
+
+ switch (event->event_type) {
+ case G_FILE_MONITOR_EVENT_CREATED:
+ tracker_monitor_emit_created (monitor, file, is_directory);
+ break;
+ case G_FILE_MONITOR_EVENT_CHANGED:
+ tracker_monitor_emit_updated (monitor, file, is_directory);
+ break;
+ case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
+ tracker_monitor_emit_attributes_updated (monitor, file, is_directory);
+ break;
+ case G_FILE_MONITOR_EVENT_DELETED:
+ tracker_monitor_emit_deleted (monitor, file, is_directory);
+ break;
+ case G_FILE_MONITOR_EVENT_MOVED:
+ tracker_monitor_emit_moved (monitor, file, other_file, is_directory);
+ break;
+ default:
+ g_warning ("Trying to emit monitor signal with unhandled event %d",
+ event->event_type);
+ break;
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
+/* Executed in monitor thread */
+static void
+queue_signal_for_event (TrackerMonitorGlib *monitor,
+ GFileMonitorEvent type,
+ gboolean is_directory,
+ GFile *file,
+ GFile *other_file)
+{
+ TrackerMonitorGlibPrivate *priv;
+ MonitorEvent *event;
+
+ priv = tracker_monitor_glib_get_instance_private (monitor);
+
+ event = monitor_event_new (monitor, file, other_file,
+ type, is_directory);
+
+ g_main_context_invoke_full (priv->thread.owner_context,
+ G_PRIORITY_HIGH,
+ (GSourceFunc) emit_signal_for_event,
+ event,
+ (GDestroyNotify) monitor_event_free);
+}
+
+/* Executed in monitor thread */
+static void
+flush_cached_event (TrackerMonitorGlib *monitor,
+ GFile *file)
+{
+ TrackerMonitorGlibPrivate *priv;
+ MonitorEvent *event;
+
+ priv = tracker_monitor_glib_get_instance_private (TRACKER_MONITOR_GLIB (monitor));
+
+ event = g_hash_table_lookup (priv->thread.cached_events, file);
+
+ if (event) {
+ queue_signal_for_event (monitor, event->event_type,
+ event->is_directory, event->file, NULL);
+ g_hash_table_remove (priv->thread.cached_events, file);
+ }
+}
+
+/* Executed in monitor thread */
+static void
+cache_event (TrackerMonitorGlib *monitor,
+ GFile *file,
+ GFileMonitorEvent event_type,
+ gboolean is_directory)
+{
+ TrackerMonitorGlibPrivate *priv;
+ MonitorEvent *event;
+
+ priv = tracker_monitor_glib_get_instance_private (monitor);
+ event = g_hash_table_lookup (priv->thread.cached_events, file);
+
+ if (!event) {
+ event = monitor_event_new (monitor, file, NULL,
+ event_type, is_directory);
+ g_hash_table_insert (priv->thread.cached_events,
+ g_object_ref (file),
+ event);
+ }
+}
+
+static gboolean
+flush_event_idle_cb (gpointer user_data)
+{
+ MonitorEvent *event = user_data;
+ TrackerMonitorGlibPrivate *priv =
+ tracker_monitor_glib_get_instance_private (event->monitor);
+
+ queue_signal_for_event (event->monitor, event->event_type,
+ event->is_directory, event->file, NULL);
+ g_hash_table_remove (priv->thread.cached_events, event->file);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+flush_event_later (TrackerMonitorGlib *monitor,
+ GFile *file)
+{
+ TrackerMonitorGlibPrivate *priv =
+ tracker_monitor_glib_get_instance_private (monitor);
+ MonitorEvent *event;
+
+ event = g_hash_table_lookup (priv->thread.cached_events, file);
+ if (!event)
+ return;
+
+ event->source = g_idle_source_new ();
+ g_source_set_callback (event->source, flush_event_idle_cb, event, NULL);
+ g_source_attach (event->source,
+ priv->thread.monitor_context);
+}
+
+/* Executed in monitor thread */
+static void
+monitor_event_cb (GFileMonitor *file_monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ gpointer user_data)
+{
+ TrackerMonitorGlib *monitor;
+ gchar *file_uri;
+ gchar *other_file_uri;
+ gboolean is_directory = FALSE;
+ TrackerMonitorGlibPrivate *priv;
+ MonitorEvent *prev_event;
+
+ monitor = user_data;
+ priv = tracker_monitor_glib_get_instance_private (monitor);
+ prev_event = g_hash_table_lookup (priv->thread.cached_events, file);
+
+ if (G_UNLIKELY (!priv->enabled)) {
+ TRACKER_NOTE (MONITORS, g_message ("Silently dropping monitor event, monitor disabled for
now"));
+ return;
+ }
+
+ /* Get URIs as paths may not be in UTF-8 */
+ file_uri = g_file_get_uri (file);
+
+ if (!other_file) {
+ is_directory = check_is_directory (monitor, file);
+
+ other_file_uri = NULL;
+ TRACKER_NOTE (MONITORS,
+ g_message ("Received monitor event:%d (%s) for %s:'%s'",
+ event_type,
+ monitor_event_to_string (event_type),
+ is_directory ? "directory" : "file",
+ file_uri));
+
+ if (is_directory &&
+ event_type == G_FILE_MONITOR_EVENT_DELETED) {
+ GFileMonitor *dir_monitor;
+
+ dir_monitor = g_hash_table_lookup (priv->thread.monitors, file);
+
+ /* We may get 2 DELETED events on directories, one from the
+ * directory monitor for the directory itself, and again from
+ * the parent folder.
+ *
+ * If the parent event is handled first, we cancel the monitor
+ * so the second event does not get to us. However if the
+ * order is inverted, just cancelling the directory monitor
+ * for the deleted directory will not stop the parent directory
+ * event. We must check explicitly for that case.
+ */
+ if (dir_monitor &&
+ dir_monitor != file_monitor &&
+ g_file_monitor_is_cancelled (dir_monitor)) {
+ g_free (file_uri);
+ return;
+ }
+ }
+ } else {
+ if (event_type == G_FILE_MONITOR_EVENT_RENAMED ||
+ event_type == G_FILE_MONITOR_EVENT_MOVED_OUT) {
+ is_directory = check_is_directory (monitor, other_file);
+ } else if (event_type == G_FILE_MONITOR_EVENT_MOVED_IN) {
+ is_directory = check_is_directory (monitor, file);
+ }
+
+ other_file_uri = g_file_get_uri (other_file);
+ TRACKER_NOTE (MONITORS,
+ g_message ("Received monitor event:%d (%s) for files '%s'->'%s'",
+ event_type,
+ monitor_event_to_string (event_type),
+ file_uri,
+ other_file_uri));
+
+ if (is_directory &&
+ (event_type == G_FILE_MONITOR_EVENT_RENAMED ||
+ event_type == G_FILE_MONITOR_EVENT_MOVED_OUT) &&
+ prev_event &&
+ prev_event->event_type == G_FILE_MONITOR_EVENT_DELETED) {
+ /* If a directory is moved, there is also an EVENT_DELETED
+ * coming from the GFileMonitor on the folder itself (as the
+ * folder being monitored no longer exists). We may receive
+ * this event before this one, we should ensure it's cleared
+ * out.
+ */
+ g_hash_table_remove (priv->thread.cached_events, file);
+ }
+ }
+
+ /* Note that in any case we should be moving the monitors
+ * here to the new place, as the new place may be ignored.
+ * We should leave this to the upper layers. But one thing
+ * we must do is actually CANCEL all these monitors. */
+ if (is_directory &&
+ (event_type == G_FILE_MONITOR_EVENT_RENAMED ||
+ event_type == G_FILE_MONITOR_EVENT_MOVED_IN ||
+ event_type == G_FILE_MONITOR_EVENT_DELETED)) {
+ monitor_cancel_recursively (monitor, file);
+ }
+
+ switch (event_type) {
+ case G_FILE_MONITOR_EVENT_CREATED:
+ case G_FILE_MONITOR_EVENT_CHANGED:
+ if (!priv->use_changed_event) {
+ cache_event (monitor, file, event_type, is_directory);
+ } else {
+ queue_signal_for_event (monitor, event_type,
+ is_directory, file, NULL);
+ }
+ break;
+ case G_FILE_MONITOR_EVENT_DELETED:
+ if (prev_event &&
+ prev_event->event_type == G_FILE_MONITOR_EVENT_CREATED) {
+ /* Consume both the cached CREATED event and this one */
+ g_hash_table_remove (priv->thread.cached_events, file);
+ break;
+ }
+
+ /* In any case, cached events are stale */
+ g_hash_table_remove (priv->thread.cached_events, file);
+
+ cache_event (monitor, file, event_type, is_directory);
+ flush_event_later (monitor, file);
+ break;
+ case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
+ queue_signal_for_event (monitor, event_type,
+ is_directory, file, NULL);
+ break;
+ case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
+ flush_cached_event (monitor, file);
+ break;
+ case G_FILE_MONITOR_EVENT_MOVED_IN:
+ if (other_file) {
+ /* Both MOVED_IN and MOVE_OUT are fine points to emit
+ * ::item-moved when source/dest are known. We choose
+ * to emit it here, and ignore the MOVE_OUT.
+ */
+ queue_signal_for_event (monitor,
+ G_FILE_MONITOR_EVENT_MOVED,
+ is_directory,
+ other_file, file);
+ } else {
+ /* No known origin, treat as a new file */
+ queue_signal_for_event (monitor,
+ G_FILE_MONITOR_EVENT_CREATED,
+ is_directory,
+ file, NULL);
+ }
+ break;
+ case G_FILE_MONITOR_EVENT_MOVED_OUT:
+ if (!other_file) {
+ /* No known destination. Treat as remove */
+ queue_signal_for_event (monitor,
+ G_FILE_MONITOR_EVENT_DELETED,
+ is_directory,
+ file, NULL);
+ }
+ break;
+ case G_FILE_MONITOR_EVENT_RENAMED:
+ queue_signal_for_event (monitor,
+ G_FILE_MONITOR_EVENT_MOVED,
+ is_directory, file, other_file);
+ break;
+ case G_FILE_MONITOR_EVENT_MOVED:
+ g_warn_if_reached ();
+ break;
+ case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
+ case G_FILE_MONITOR_EVENT_UNMOUNTED:
+ break;
+ }
+
+ g_free (file_uri);
+ g_free (other_file_uri);
+}
+
+static GFileMonitor *
+directory_monitor_new (TrackerMonitorGlib *monitor,
+ GFile *file)
+{
+ GFileMonitor *file_monitor;
+ GError *error = NULL;
+
+ file_monitor = g_file_monitor_directory (file,
+ G_FILE_MONITOR_WATCH_MOVES,
+ NULL,
+ &error);
+
+ if (error) {
+ gchar *uri;
+
+ uri = g_file_get_uri (file);
+ g_warning ("Could not add monitor for path:'%s', %s",
+ uri, error->message);
+
+ g_error_free (error);
+ g_free (uri);
+
+ return NULL;
+ }
+
+ g_signal_connect (file_monitor, "changed",
+ G_CALLBACK (monitor_event_cb),
+ monitor);
+
+ return file_monitor;
+}
+
+static void
+directory_monitor_cancel (GFileMonitor *monitor)
+{
+ if (monitor) {
+ g_file_monitor_cancel (G_FILE_MONITOR (monitor));
+ g_object_unref (monitor);
+ }
+}
+
+static void
+tracker_monitor_glib_set_enabled (TrackerMonitorGlib *monitor,
+ gboolean enabled)
+{
+ TrackerMonitorGlibPrivate *priv;
+ MonitorRequest *request;
+
+ priv = tracker_monitor_glib_get_instance_private (monitor);
+
+ /* Don't replace all monitors if we are already
+ * enabled/disabled.
+ */
+ if (priv->enabled == enabled) {
+ return;
+ }
+
+ priv->enabled = enabled;
+ g_object_notify (G_OBJECT (monitor), "enabled");
+
+ request = g_new0 (MonitorRequest, 1);
+ request->monitor = monitor;
+ request->files = g_hash_table_get_keys (priv->monitored_dirs);
+ g_list_foreach (request->files, (GFunc) g_object_ref, NULL);
+ request->type = enabled ? MONITOR_REQUEST_ADD : MONITOR_REQUEST_REMOVE;
+
+ monitor_request_queue (monitor, request);
+ block_for_requests (monitor);
+}
+
+static gboolean
+tracker_monitor_glib_add (TrackerMonitor *monitor,
+ GFile *file)
+{
+ TrackerMonitorGlibPrivate *priv;
+ gchar *uri;
+
+ priv = tracker_monitor_glib_get_instance_private (TRACKER_MONITOR_GLIB (monitor));
+
+ if (g_hash_table_lookup (priv->monitored_dirs, file)) {
+ return TRUE;
+ }
+
+ /* Cap the number of monitors */
+ if (g_hash_table_size (priv->monitored_dirs) >= priv->monitor_limit) {
+ priv->monitors_ignored++;
+
+ if (!priv->monitor_limit_warned) {
+ g_warning ("The maximum number of monitors to set (%d) "
+ "has been reached, not adding any new ones",
+ priv->monitor_limit);
+ priv->monitor_limit_warned = TRUE;
+ }
+
+ return FALSE;
+ }
+
+ uri = g_file_get_uri (file);
+
+ if (priv->enabled) {
+ /* We don't check if a file exists or not since we might want
+ * to monitor locations which don't exist yet.
+ *
+ * Also, we assume ALL paths passed are directories.
+ */
+ MonitorRequest *request;
+
+ request = g_new0 (MonitorRequest, 1);
+ request->monitor = TRACKER_MONITOR_GLIB (monitor);
+ request->files = g_list_prepend (NULL, g_object_ref (file));
+ request->type = MONITOR_REQUEST_ADD;
+
+ monitor_request_queue (TRACKER_MONITOR_GLIB (monitor), request);
+ block_for_requests (TRACKER_MONITOR_GLIB (monitor));
+ }
+
+ g_hash_table_add (priv->monitored_dirs, g_object_ref (file));
+
+ TRACKER_NOTE (MONITORS, g_message ("Added monitor for path:'%s', total monitors:%d",
+ uri,
+ g_hash_table_size (priv->monitored_dirs)));
+
+ g_free (uri);
+
+ return TRUE;
+}
+
+static gboolean
+tracker_monitor_glib_remove (TrackerMonitor *monitor,
+ GFile *file)
+{
+ TrackerMonitorGlibPrivate *priv;
+ gboolean removed;
+
+ priv = tracker_monitor_glib_get_instance_private (TRACKER_MONITOR_GLIB (monitor));
+ removed = g_hash_table_remove (priv->monitored_dirs, file);
+
+ if (removed) {
+ MonitorRequest *request;
+ gchar *uri;
+
+ request = g_new0 (MonitorRequest, 1);
+ request->monitor = TRACKER_MONITOR_GLIB (monitor);
+ request->files = g_list_prepend (NULL, g_object_ref (file));
+ request->type = MONITOR_REQUEST_REMOVE;
+
+ monitor_request_queue (TRACKER_MONITOR_GLIB (monitor), request);
+ block_for_requests (TRACKER_MONITOR_GLIB (monitor));
+
+ uri = g_file_get_uri (file);
+ TRACKER_NOTE (MONITORS, g_message ("Removed monitor for path:'%s', total monitors:%d",
+ uri,
+ g_hash_table_size (priv->monitored_dirs)));
+
+ g_free (uri);
+ }
+
+ return removed;
+}
+
+/* If @is_strict is %TRUE, return %TRUE iff @file is a child of @prefix.
+ * If @is_strict is %FALSE, additionally return %TRUE if @file equals @prefix.
+ */
+static gboolean
+file_has_maybe_strict_prefix (GFile *file,
+ GFile *prefix,
+ gboolean is_strict)
+{
+ return (g_file_has_prefix (file, prefix) ||
+ (!is_strict && g_file_equal (file, prefix)));
+}
+
+static gboolean
+remove_recursively (TrackerMonitorGlib *monitor,
+ GFile *file,
+ gboolean remove_top_level)
+{
+ TrackerMonitorGlibPrivate *priv;
+ GHashTableIter iter;
+ MonitorRequest *request;
+ gpointer iter_file;
+ guint items_removed = 0;
+ gchar *uri;
+
+ g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), FALSE);
+ g_return_val_if_fail (G_IS_FILE (file), FALSE);
+
+ priv = tracker_monitor_glib_get_instance_private (monitor);
+
+ request = g_new0 (MonitorRequest, 1);
+ request->monitor = monitor;
+ request->type = MONITOR_REQUEST_REMOVE;
+
+ g_hash_table_iter_init (&iter, priv->monitored_dirs);
+ while (g_hash_table_iter_next (&iter, &iter_file, NULL)) {
+ if (!file_has_maybe_strict_prefix (iter_file, file,
+ !remove_top_level)) {
+ continue;
+ }
+
+ request->files = g_list_prepend (request->files, g_object_ref (file));
+ g_hash_table_iter_remove (&iter);
+ items_removed++;
+ }
+
+ uri = g_file_get_uri (file);
+ TRACKER_NOTE (MONITORS,
+ g_message ("Removed all monitors %srecursively for path:'%s', )"
+ "total monitors:%d",
+ !remove_top_level ? "(except top level) " : "",
+ uri, g_hash_table_size (priv->monitored_dirs)));
+ g_free (uri);
+
+ monitor_request_queue (monitor, request);
+ block_for_requests (monitor);
+
+ if (items_removed > 0) {
+ /* We reset this because now it is possible we have limit - 1 */
+ priv->monitor_limit_warned = FALSE;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+tracker_monitor_glib_remove_recursively (TrackerMonitor *monitor,
+ GFile *file,
+ gboolean only_children)
+{
+ return remove_recursively (TRACKER_MONITOR_GLIB (monitor), file, !only_children);
+}
+
+/* Runs in the monitor thread */
+static gboolean
+monitor_cancel_recursively (TrackerMonitorGlib *monitor,
+ GFile *file)
+{
+ TrackerMonitorGlibPrivate *priv;
+ GHashTableIter iter;
+ gpointer iter_file, iter_file_monitor;
+ guint items_cancelled = 0;
+
+ priv = tracker_monitor_glib_get_instance_private (monitor);
+
+ g_hash_table_iter_init (&iter, priv->thread.monitors);
+ while (g_hash_table_iter_next (&iter, &iter_file, &iter_file_monitor)) {
+ gchar *uri;
+
+ if (!g_file_has_prefix (iter_file, file) &&
+ !g_file_equal (iter_file, file)) {
+ continue;
+ }
+
+ uri = g_file_get_uri (iter_file);
+ g_file_monitor_cancel (G_FILE_MONITOR (iter_file_monitor));
+ TRACKER_NOTE (MONITORS, g_message ("Cancelled monitor for path:'%s'", uri));
+ g_free (uri);
+
+ items_cancelled++;
+ }
+
+ return items_cancelled > 0;
+}
+
+static gboolean
+tracker_monitor_glib_is_watched (TrackerMonitor *monitor,
+ GFile *file)
+{
+ TrackerMonitorGlibPrivate *priv;
+
+ priv = tracker_monitor_glib_get_instance_private (TRACKER_MONITOR_GLIB (monitor));
+
+ if (!priv->enabled)
+ return FALSE;
+
+ return g_hash_table_contains (priv->monitored_dirs, file);
+}
+
+TrackerMonitor *
+tracker_monitor_glib_new (GError **error)
+{
+ return g_initable_new (TRACKER_TYPE_MONITOR_GLIB,
+ NULL, error, NULL);
+}
diff --git a/src/libtracker-miner/tracker-monitor-glib.h b/src/libtracker-miner/tracker-monitor-glib.h
new file mode 100644
index 000000000..b72eaeef2
--- /dev/null
+++ b/src/libtracker-miner/tracker-monitor-glib.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2021, Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Author: Carlos Garnacho <carlosg gnome org>
+ */
+
+#ifndef __LIBTRACKER_MINER_MONITOR_GLIB_H__
+#define __LIBTRACKER_MINER_MONITOR_GLIB_H__
+
+#if !defined (__LIBTRACKER_MINER_H_INSIDE__) && !defined (TRACKER_COMPILATION)
+#error "Only <libtracker-miner/tracker-miner.h> can be included directly."
+#endif
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "tracker-monitor.h"
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_MONITOR_GLIB (tracker_monitor_glib_get_type ())
+G_DECLARE_FINAL_TYPE (TrackerMonitorGlib, tracker_monitor_glib,
+ TRACKER, MONITOR_GLIB,
+ TrackerMonitor)
+
+G_END_DECLS
+
+#endif /* __LIBTRACKER_MINER_MONITOR_GLIB_H__ */
diff --git a/src/libtracker-miner/tracker-monitor.c b/src/libtracker-miner/tracker-monitor.c
index 47289afcf..dc653ce8c 100644
--- a/src/libtracker-miner/tracker-monitor.c
+++ b/src/libtracker-miner/tracker-monitor.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009, Nokia <ivan frade nokia com>
+ * Copyright (C) 2021, Red Hat Inc
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -15,85 +15,16 @@
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
+ *
+ * Author: Carlos Garnacho <carlosg gnome org>
*/
#include "config-miners.h"
-#include <stdlib.h>
-#include <string.h>
-#include <gio/gio.h>
-
-#if defined (__OpenBSD__) || defined (__FreeBSD__) || defined (__NetBSD__) || defined (__APPLE__)
-#include <sys/types.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#define TRACKER_MONITOR_KQUEUE
-#endif
-
#include "tracker-monitor.h"
+#include "tracker-monitor-private.h"
-#include "libtracker-miners-common/tracker-debug.h"
-
-typedef struct TrackerMonitorPrivate TrackerMonitorPrivate;
-
-struct TrackerMonitorPrivate {
- GHashTable *monitored_dirs;
-
- gboolean enabled;
-
- guint monitor_limit;
- gboolean monitor_limit_warned;
- guint monitors_ignored;
-
- /* For FAM, the _CHANGES_DONE event is not signalled, so we
- * have to just use the _CHANGED event instead.
- */
- gboolean use_changed_event;
-
- TrackerIndexingTree *tree;
-
- struct {
- GMainContext *owner_context;
- GMainContext *monitor_context;
- GMainLoop *monitor_thread_loop;
- GThread *monitor_thread;
- GHashTable *cached_events;
- GHashTable *monitors;
- GMutex mutex;
- GCond cond;
- gint n_requests;
- } thread;
-};
-
-typedef struct {
- GFile *file;
- gchar *file_uri;
- GFile *other_file;
- gchar *other_file_uri;
- gboolean is_directory;
- guint32 event_type;
- gboolean expirable;
-} EventData;
-
-typedef struct {
- TrackerMonitor *monitor;
- GFile *file;
- GFile *other_file;
- GSource *source;
- gboolean is_directory;
- GFileMonitorEvent event_type;
-} MonitorEvent;
-
-typedef enum {
- MONITOR_REQUEST_ADD,
- MONITOR_REQUEST_REMOVE,
-} MonitorRequestType;
-
-typedef struct {
- TrackerMonitor *monitor;
- MonitorRequestType type;
- GList *files;
-} MonitorRequest;
+#include "tracker-monitor-glib.h"
enum {
ITEM_CREATED,
@@ -106,166 +37,52 @@ enum {
enum {
PROP_0,
- PROP_ENABLED
+ PROP_ENABLED,
+ PROP_LIMIT,
+ PROP_COUNT,
+ PROP_IGNORED,
+ N_PROPS
};
-static void tracker_monitor_finalize (GObject *object);
-static void tracker_monitor_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec);
-static void tracker_monitor_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec);
-static guint get_kqueue_limit (void);
-static guint get_inotify_limit (void);
-static GFileMonitor * directory_monitor_new (TrackerMonitor *monitor,
- GFile *file);
-static void directory_monitor_cancel (GFileMonitor *dir_monitor);
-
-
-static gboolean monitor_cancel_recursively (TrackerMonitor *monitor,
- GFile *file);
-
static guint signals[LAST_SIGNAL] = { 0, };
+static GParamSpec *pspecs[N_PROPS] = { 0, };
-static void tracker_monitor_initable_iface_init (GInitableIface *iface);
-
-G_DEFINE_TYPE_WITH_CODE (TrackerMonitor, tracker_monitor, G_TYPE_OBJECT,
- G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
- tracker_monitor_initable_iface_init)
- G_ADD_PRIVATE (TrackerMonitor))
+G_DEFINE_ABSTRACT_TYPE (TrackerMonitor, tracker_monitor, G_TYPE_OBJECT)
-static gpointer
-monitor_thread_func (gpointer user_data)
-{
- TrackerMonitor *monitor = user_data;
- TrackerMonitorPrivate *priv;
-
- priv = tracker_monitor_get_instance_private (monitor);
- g_main_context_push_thread_default (priv->thread.monitor_context);
- g_main_loop_run (priv->thread.monitor_thread_loop);
- g_main_context_pop_thread_default (priv->thread.monitor_context);
- g_main_loop_unref (priv->thread.monitor_thread_loop);
-
- return NULL;
-}
-
-static gboolean
-tracker_monitor_initable_init (GInitable *initable,
- GCancellable *cancellable,
- GError **error)
+static void
+tracker_monitor_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
{
- GType monitor_backend;
- const gchar *name;
- GError *inner_error = NULL;
- TrackerMonitorPrivate *priv;
- GFile *file;
- GFileMonitor *monitor;
-
- priv = tracker_monitor_get_instance_private (TRACKER_MONITOR (initable));
-
- /* For the first monitor we get the type and find out if we
- * are using inotify, FAM, polling, etc.
- */
- file = g_file_new_for_path (g_get_home_dir ());
- monitor = g_file_monitor_directory (file,
- G_FILE_MONITOR_WATCH_MOVES,
- NULL,
- &inner_error);
- if (inner_error) {
- g_propagate_error (error, inner_error);
- return FALSE;
- }
-
- monitor_backend = G_OBJECT_TYPE (monitor);
-
- /* We use the name because the type itself is actually
- * private and not available publically. Note this is
- * subject to change, but unlikely of course.
- */
- name = g_type_name (monitor_backend);
-
- /* Set limits based on backend... */
- if (strcmp (name, "GInotifyDirectoryMonitor") == 0 ||
- strcmp (name, "GInotifyFileMonitor") == 0) {
- /* Using inotify */
- TRACKER_NOTE (MONITORS, g_message ("Monitor backend is Inotify"));
-
- /* Setting limit based on kernel
- * settings in /proc...
- */
- priv->monitor_limit = get_inotify_limit ();
-
- /* We don't use 100% of the monitors, we allow other
- * applications to have at least 500 or so to use
- * between them selves. This only
- * applies to inotify because it is a
- * user shared resource.
- */
- priv->monitor_limit -= 500;
-
- /* Make sure we don't end up with a
- * negative maximum.
- */
- priv->monitor_limit = MAX (priv->monitor_limit, 0);
- } else if (strcmp (name, "GKqueueDirectoryMonitor") == 0 ||
- strcmp (name, "GKqueueFileMonitor") == 0) {
- /* Using kqueue(2) */
- TRACKER_NOTE (MONITORS, g_message ("Monitor backend is kqueue"));
-
- priv->monitor_limit = get_kqueue_limit ();
- } else if (strcmp (name, "GFamDirectoryMonitor") == 0) {
- /* Using Fam */
- TRACKER_NOTE (MONITORS, g_message ("Monitor backend is Fam"));
-
- /* Setting limit to an arbitary limit
- * based on testing
- */
- priv->monitor_limit = 400;
- priv->use_changed_event = TRUE;
- } else if (strcmp (name, "GWin32DirectoryMonitor") == 0) {
- /* Using Windows */
- TRACKER_NOTE (MONITORS, g_message ("Monitor backend is Windows"));
-
- /* Guessing limit... */
- priv->monitor_limit = 8192;
- } else {
- /* Unknown */
- g_warning ("Monitor backend:'%s' is unhandled. Monitoring will be disabled",
- name);
- priv->enabled = FALSE;
- }
-
- if (priv->enabled)
- TRACKER_NOTE (MONITORS, g_message ("Monitor limit is %d", priv->monitor_limit));
-
- g_file_monitor_cancel (monitor);
- g_object_unref (monitor);
- g_object_unref (file);
-
- priv->thread.owner_context = g_main_context_ref_thread_default ();
- priv->thread.monitor_context = g_main_context_new ();
- priv->thread.monitor_thread_loop = g_main_loop_new (priv->thread.monitor_context, FALSE);
-
- priv->thread.monitor_thread =
- g_thread_try_new ("Monitor thread",
- monitor_thread_func,
- initable,
- &inner_error);
- if (inner_error) {
- g_propagate_error (error, inner_error);
- return FALSE;
+ switch (prop_id) {
+ case PROP_ENABLED:
+ case PROP_LIMIT:
+ case PROP_COUNT:
+ case PROP_IGNORED:
+ g_warning ("Property should be overridden by superclass");
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
-
- return TRUE;
}
static void
-tracker_monitor_initable_iface_init (GInitableIface *iface)
+tracker_monitor_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
{
- iface->init = tracker_monitor_initable_init;
+ switch (prop_id) {
+ case PROP_ENABLED:
+ case PROP_LIMIT:
+ case PROP_COUNT:
+ case PROP_IGNORED:
+ g_warning ("Property should be overridden by superclass");
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
}
static void
@@ -275,7 +92,6 @@ tracker_monitor_class_init (TrackerMonitorClass *klass)
object_class = G_OBJECT_CLASS (klass);
- object_class->finalize = tracker_monitor_finalize;
object_class->set_property = tracker_monitor_set_property;
object_class->get_property = tracker_monitor_get_property;
@@ -337,299 +153,47 @@ tracker_monitor_class_init (TrackerMonitorClass *klass)
G_TYPE_BOOLEAN,
G_TYPE_BOOLEAN);
- g_object_class_install_property (object_class,
- PROP_ENABLED,
- g_param_spec_boolean ("enabled",
- "Enabled",
- "Enabled",
- TRUE,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
G_PARAM_STATIC_STRINGS));
-}
-
-static MonitorEvent *
-monitor_event_new (TrackerMonitor *monitor,
- GFile *file,
- GFile *other_file,
- GFileMonitorEvent event_type,
- gboolean is_directory)
-{
- MonitorEvent *event;
-
- event = g_new0 (MonitorEvent, 1);
- event->monitor = g_object_ref (monitor);
- event->file = g_object_ref (file);
- g_set_object (&event->other_file, other_file);
- event->event_type = event_type;
- event->is_directory = is_directory;
-
- return event;
-}
-
-static void
-monitor_event_free (MonitorEvent *event)
-{
- g_object_unref (event->monitor);
- g_object_unref (event->file);
- g_clear_object (&event->other_file);
- g_clear_pointer (&event->source, g_source_destroy);
- g_free (event);
+ pspecs[PROP_ENABLED] =
+ g_param_spec_boolean ("enabled",
+ "Enabled",
+ "Enabled",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS);
+ pspecs[PROP_LIMIT] =
+ g_param_spec_uint ("limit",
+ "Limit",
+ "Limit",
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS);
+ pspecs[PROP_COUNT] =
+ g_param_spec_uint ("count",
+ "Count",
+ "Count",
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS);
+ pspecs[PROP_IGNORED] =
+ g_param_spec_uint ("ignored",
+ "Ignored",
+ "Ignored",
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, N_PROPS, pspecs);
}
static void
tracker_monitor_init (TrackerMonitor *object)
{
- TrackerMonitorPrivate *priv;
-
- priv = tracker_monitor_get_instance_private (object);
-
- /* By default we enable monitoring */
- priv->enabled = TRUE;
-
- /* Create monitors table for this module */
- priv->monitored_dirs =
- g_hash_table_new_full (g_file_hash,
- (GEqualFunc) g_file_equal,
- (GDestroyNotify) g_object_unref,
- NULL);
-
- priv->thread.cached_events =
- g_hash_table_new_full (g_file_hash,
- (GEqualFunc) g_file_equal,
- g_object_unref,
- (GDestroyNotify) monitor_event_free);
-
- priv->thread.monitors =
- g_hash_table_new_full (g_file_hash,
- (GEqualFunc) g_file_equal,
- (GDestroyNotify) g_object_unref,
- (GDestroyNotify) directory_monitor_cancel);
-
- g_mutex_init (&priv->thread.mutex);
- g_cond_init (&priv->thread.cond);
-}
-
-static gboolean
-quit_thread (TrackerMonitor *monitor)
-{
- TrackerMonitorPrivate *priv;
-
- priv = tracker_monitor_get_instance_private (monitor);
- g_main_loop_quit (priv->thread.monitor_thread_loop);
-
- return G_SOURCE_REMOVE;
-}
-
-static void
-tracker_monitor_finalize (GObject *object)
-{
- TrackerMonitorPrivate *priv;
-
- priv = tracker_monitor_get_instance_private (TRACKER_MONITOR (object));
-
- if (priv->thread.monitor_thread_loop) {
- g_main_context_invoke_full (priv->thread.monitor_context,
- G_PRIORITY_HIGH,
- (GSourceFunc) quit_thread,
- object, NULL);
- }
-
- if (priv->thread.monitor_thread)
- g_thread_join (priv->thread.monitor_thread);
-
- g_clear_pointer (&priv->thread.monitor_context, g_main_context_unref);
- g_clear_pointer (&priv->thread.owner_context, g_main_context_unref);
- g_clear_pointer (&priv->thread.cached_events, g_hash_table_unref);
- g_clear_pointer (&priv->thread.monitors, g_hash_table_unref);
-
- g_hash_table_unref (priv->monitored_dirs);
-
- G_OBJECT_CLASS (tracker_monitor_parent_class)->finalize (object);
-}
-
-static void
-tracker_monitor_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- switch (prop_id) {
- case PROP_ENABLED:
- tracker_monitor_set_enabled (TRACKER_MONITOR (object),
- g_value_get_boolean (value));
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- }
-}
-
-static void
-tracker_monitor_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
-{
- TrackerMonitorPrivate *priv;
-
- priv = tracker_monitor_get_instance_private (TRACKER_MONITOR (object));
-
- switch (prop_id) {
- case PROP_ENABLED:
- g_value_set_boolean (value, priv->enabled);
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- }
-}
-
-static guint
-get_kqueue_limit (void)
-{
- guint limit = 400;
-
-#ifdef TRACKER_MONITOR_KQUEUE
- struct rlimit rl;
- if (getrlimit (RLIMIT_NOFILE, &rl) == 0) {
- rl.rlim_cur = rl.rlim_max;
- } else {
- return limit;
- }
-
- if (setrlimit(RLIMIT_NOFILE, &rl) == 0)
- limit = (rl.rlim_cur * 90) / 100;
-#endif /* TRACKER_MONITOR_KQUEUE */
-
- return limit;
-}
-
-static guint
-get_inotify_limit (void)
-{
- GError *error = NULL;
- const gchar *filename;
- gchar *contents = NULL;
- guint limit;
-
- filename = "/proc/sys/fs/inotify/max_user_watches";
-
- if (!g_file_get_contents (filename,
- &contents,
- NULL,
- &error)) {
- g_warning ("Couldn't get INotify monitor limit from:'%s', %s",
- filename,
- error ? error->message : "no error given");
- g_clear_error (&error);
-
- /* Setting limit to an arbitary limit */
- limit = 8192;
- } else {
- limit = atoi (contents);
- g_free (contents);
- }
-
- return limit;
-}
-
-static gboolean
-check_is_directory (TrackerMonitor *monitor,
- GFile *file)
-{
- GFileType file_type;
-
- file_type = g_file_query_file_type (file, G_FILE_QUERY_INFO_NONE, NULL);
-
- if (file_type == G_FILE_TYPE_DIRECTORY)
- return TRUE;
-
- if (file_type == G_FILE_TYPE_UNKNOWN) {
- TrackerMonitorPrivate *priv;
-
- priv = tracker_monitor_get_instance_private (monitor);
-
- /* Whatever it was, it's gone. Check the monitors
- * hashtable to know whether it was a directory
- * we knew about
- */
- if (g_hash_table_lookup (priv->thread.monitors, file) != NULL)
- return TRUE;
- }
-
- return FALSE;
-}
-
-/* Executed in monitor thread */
-static gboolean
-monitor_request_execute (MonitorRequest *request)
-{
- TrackerMonitorPrivate *priv;
-
- priv = tracker_monitor_get_instance_private (request->monitor);
-
- g_mutex_lock (&priv->thread.mutex);
-
- while (request->files) {
- GFile *file = request->files->data;
-
- if (request->type == MONITOR_REQUEST_ADD) {
- GFileMonitor *monitor;
-
- monitor = directory_monitor_new (request->monitor,
- file);
- if (monitor) {
- g_hash_table_insert (priv->thread.monitors,
- g_object_ref (file),
- monitor);
- }
- } else if (request->type == MONITOR_REQUEST_REMOVE) {
- g_hash_table_remove (priv->thread.monitors,
- file);
- } else {
- g_assert_not_reached ();
- }
-
- request->files = g_list_remove (request->files, file);
- g_object_unref (file);
- }
-
- if (g_atomic_int_dec_and_test (&priv->thread.n_requests))
- g_cond_signal (&priv->thread.cond);
-
- g_mutex_unlock (&priv->thread.mutex);
-
- return G_SOURCE_REMOVE;
-}
-
-/* Executed in main thread */
-static void
-monitor_request_queue (TrackerMonitor *monitor,
- MonitorRequest *request)
-{
- TrackerMonitorPrivate *priv;
-
- priv = tracker_monitor_get_instance_private (request->monitor);
-
- g_atomic_int_inc (&priv->thread.n_requests);
- g_main_context_invoke_full (priv->thread.monitor_context,
- G_PRIORITY_DEFAULT,
- (GSourceFunc) monitor_request_execute,
- request, g_free);
-}
-
-static void
-block_for_requests (TrackerMonitor *monitor)
-{
- TrackerMonitorPrivate *priv;
-
- priv = tracker_monitor_get_instance_private (monitor);
-
- g_mutex_lock (&priv->thread.mutex);
-
- while (g_atomic_int_get (&priv->thread.n_requests) != 0)
- g_cond_wait (&priv->thread.cond, &priv->thread.mutex);
-
- g_mutex_unlock (&priv->thread.mutex);
}
gboolean
@@ -637,785 +201,184 @@ tracker_monitor_move (TrackerMonitor *monitor,
GFile *old_file,
GFile *new_file)
{
- TrackerMonitorPrivate *priv;
- GHashTableIter iter;
- MonitorRequest *request;
- gchar *old_prefix;
- gpointer iter_file;
- guint items_moved = 0;
-
- priv = tracker_monitor_get_instance_private (monitor);
-
- /* So this is tricky. What we have to do is:
- *
- * 1) Add all monitors for the new_file directory hierarchy
- * 2) Then remove the monitors for old_file
- *
- * This order is necessary because inotify can reuse watch
- * descriptors, and libinotify will remove handles
- * asynchronously on IN_IGNORE, so the opposite sequence
- * may possibly remove valid, just added, monitors.
- */
- request = g_new0 (MonitorRequest, 1);
- request->monitor = monitor;
- request->type = MONITOR_REQUEST_ADD;
-
- old_prefix = g_file_get_path (old_file);
-
- /* Find out which subdirectories should have a file monitor added */
- g_hash_table_iter_init (&iter, priv->monitored_dirs);
- while (g_hash_table_iter_next (&iter, &iter_file, NULL)) {
- GFile *f;
- gchar *old_path, *new_path;
- gchar *new_prefix;
- gchar *p;
-
- if (!g_file_has_prefix (iter_file, old_file) &&
- !g_file_equal (iter_file, old_file)) {
- continue;
- }
-
- old_path = g_file_get_path (iter_file);
- p = strstr (old_path, old_prefix);
-
- if (!p || strcmp (p, old_prefix) == 0) {
- g_free (old_path);
- continue;
- }
-
- /* Move to end of prefix */
- p += strlen (old_prefix) + 1;
-
- /* Check this is not the end of the string */
- if (*p == '\0') {
- g_free (old_path);
- continue;
- }
-
- new_prefix = g_file_get_path (new_file);
- new_path = g_build_path (G_DIR_SEPARATOR_S, new_prefix, p, NULL);
- g_free (new_prefix);
-
- f = g_file_new_for_path (new_path);
- g_free (new_path);
-
- request->files = g_list_prepend (request->files, g_object_ref (f));
-
- g_object_unref (f);
- g_free (old_path);
- items_moved++;
- }
-
- /* Add a new monitor for the top level directory */
- tracker_monitor_add (monitor, new_file);
-
- /* Add new monitors for all subdirectories */
- monitor_request_queue (monitor, request);
-
- /* Remove the monitor for the old top level directory hierarchy */
- tracker_monitor_remove_recursively (monitor, old_file);
-
- g_free (old_prefix);
-
- block_for_requests (monitor);
-
- return items_moved > 0;
-}
-
-static const gchar *
-monitor_event_to_string (GFileMonitorEvent event_type)
-{
- switch (event_type) {
- case G_FILE_MONITOR_EVENT_CHANGED:
- return "G_FILE_MONITOR_EVENT_CHANGED";
- case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
- return "G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT";
- case G_FILE_MONITOR_EVENT_DELETED:
- return "G_FILE_MONITOR_EVENT_DELETED";
- case G_FILE_MONITOR_EVENT_CREATED:
- return "G_FILE_MONITOR_EVENT_CREATED";
- case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
- return "G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED";
- case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
- return "G_FILE_MONITOR_EVENT_PRE_UNMOUNT";
- case G_FILE_MONITOR_EVENT_UNMOUNTED:
- return "G_FILE_MONITOR_EVENT_UNMOUNTED";
- case G_FILE_MONITOR_EVENT_MOVED:
- return "G_FILE_MONITOR_EVENT_MOVED";
- case G_FILE_MONITOR_EVENT_RENAMED:
- return "G_FILE_MONITOR_EVENT_RENAMED";
- case G_FILE_MONITOR_EVENT_MOVED_IN:
- return "G_FILE_MONITOR_EVENT_MOVED_IN";
- case G_FILE_MONITOR_EVENT_MOVED_OUT:
- return "G_FILE_MONITOR_EVENT_MOVED_OUT";
- break;
- }
-
- return "unknown";
-}
-
-/* Executed in main thread */
-static gboolean
-emit_signal_for_event (MonitorEvent *event)
-{
- TrackerMonitor *monitor = event->monitor;
- gboolean is_directory = event->is_directory;
- GFile *file = event->file;
- GFile *other_file = event->other_file;
-
- switch (event->event_type) {
- case G_FILE_MONITOR_EVENT_CREATED:
- g_signal_emit (monitor,
- signals[ITEM_CREATED], 0,
- file, is_directory);
- break;
- case G_FILE_MONITOR_EVENT_CHANGED:
- g_signal_emit (monitor,
- signals[ITEM_UPDATED], 0,
- file, is_directory);
- break;
- case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
- g_signal_emit (monitor,
- signals[ITEM_ATTRIBUTE_UPDATED], 0,
- file, is_directory);
- break;
- case G_FILE_MONITOR_EVENT_DELETED:
- g_signal_emit (monitor,
- signals[ITEM_DELETED], 0,
- file, is_directory);
- break;
- case G_FILE_MONITOR_EVENT_MOVED:
- g_signal_emit (monitor,
- signals[ITEM_MOVED], 0,
- file, other_file, is_directory, TRUE);
- break;
- default:
- g_warning ("Trying to emit monitor signal with unhandled event %d",
- event->event_type);
- break;
- }
-
- return G_SOURCE_REMOVE;
-}
-
-/* Executed in monitor thread */
-static void
-queue_signal_for_event (TrackerMonitor *monitor,
- GFileMonitorEvent type,
- gboolean is_directory,
- GFile *file,
- GFile *other_file)
-{
- TrackerMonitorPrivate *priv;
- MonitorEvent *event;
-
- priv = tracker_monitor_get_instance_private (monitor);
-
- event = monitor_event_new (monitor, file, other_file,
- type, is_directory);
-
- g_main_context_invoke_full (priv->thread.owner_context,
- G_PRIORITY_HIGH,
- (GSourceFunc) emit_signal_for_event,
- event,
- (GDestroyNotify) monitor_event_free);
-}
-
-/* Executed in monitor thread */
-static void
-flush_cached_event (TrackerMonitor *monitor,
- GFile *file)
-{
- TrackerMonitorPrivate *priv;
- MonitorEvent *event;
-
- priv = tracker_monitor_get_instance_private (monitor);
-
- event = g_hash_table_lookup (priv->thread.cached_events, file);
-
- if (event) {
- queue_signal_for_event (monitor, event->event_type,
- event->is_directory, event->file, NULL);
- g_hash_table_remove (priv->thread.cached_events, file);
- }
-}
-
-/* Executed in monitor thread */
-static void
-cache_event (TrackerMonitor *monitor,
- GFile *file,
- GFileMonitorEvent event_type,
- gboolean is_directory)
-{
- TrackerMonitorPrivate *priv;
- MonitorEvent *event;
-
- priv = tracker_monitor_get_instance_private (monitor);
- event = g_hash_table_lookup (priv->thread.cached_events, file);
-
- if (!event) {
- event = monitor_event_new (monitor, file, NULL,
- event_type, is_directory);
- g_hash_table_insert (priv->thread.cached_events,
- g_object_ref (file),
- event);
- }
-}
-
-static gboolean
-flush_event_idle_cb (gpointer user_data)
-{
- MonitorEvent *event = user_data;
- TrackerMonitorPrivate *priv = tracker_monitor_get_instance_private (event->monitor);
-
- queue_signal_for_event (event->monitor, event->event_type,
- event->is_directory, event->file, NULL);
- g_hash_table_remove (priv->thread.cached_events, event->file);
-
- return G_SOURCE_REMOVE;
-}
-
-static void
-flush_event_later (TrackerMonitor *monitor,
- GFile *file)
-{
- TrackerMonitorPrivate *priv = tracker_monitor_get_instance_private (monitor);
- MonitorEvent *event;
-
- event = g_hash_table_lookup (priv->thread.cached_events, file);
- if (!event)
- return;
-
- event->source = g_idle_source_new ();
- g_source_set_callback (event->source, flush_event_idle_cb, event, NULL);
- g_source_attach (event->source,
- priv->thread.monitor_context);
-}
-
-/* Executed in monitor thread */
-static void
-monitor_event_cb (GFileMonitor *file_monitor,
- GFile *file,
- GFile *other_file,
- GFileMonitorEvent event_type,
- gpointer user_data)
-{
- TrackerMonitor *monitor;
- gchar *file_uri;
- gchar *other_file_uri;
- gboolean is_directory = FALSE;
- TrackerMonitorPrivate *priv;
- MonitorEvent *prev_event;
-
- monitor = user_data;
- priv = tracker_monitor_get_instance_private (monitor);
- prev_event = g_hash_table_lookup (priv->thread.cached_events, file);
-
- if (G_UNLIKELY (!priv->enabled)) {
- TRACKER_NOTE (MONITORS, g_message ("Silently dropping monitor event, monitor disabled for
now"));
- return;
- }
-
- /* Get URIs as paths may not be in UTF-8 */
- file_uri = g_file_get_uri (file);
-
- if (!other_file) {
- is_directory = check_is_directory (monitor, file);
-
- other_file_uri = NULL;
- TRACKER_NOTE (MONITORS,
- g_message ("Received monitor event:%d (%s) for %s:'%s'",
- event_type,
- monitor_event_to_string (event_type),
- is_directory ? "directory" : "file",
- file_uri));
-
- if (is_directory &&
- event_type == G_FILE_MONITOR_EVENT_DELETED) {
- GFileMonitor *dir_monitor;
-
- dir_monitor = g_hash_table_lookup (priv->thread.monitors, file);
-
- /* We may get 2 DELETED events on directories, one from the
- * directory monitor for the directory itself, and again from
- * the parent folder.
- *
- * If the parent event is handled first, we cancel the monitor
- * so the second event does not get to us. However if the
- * order is inverted, just cancelling the directory monitor
- * for the deleted directory will not stop the parent directory
- * event. We must check explicitly for that case.
- */
- if (dir_monitor &&
- dir_monitor != file_monitor &&
- g_file_monitor_is_cancelled (dir_monitor)) {
- g_free (file_uri);
- return;
- }
- }
- } else {
- if (event_type == G_FILE_MONITOR_EVENT_RENAMED ||
- event_type == G_FILE_MONITOR_EVENT_MOVED_OUT) {
- is_directory = check_is_directory (monitor, other_file);
- } else if (event_type == G_FILE_MONITOR_EVENT_MOVED_IN) {
- is_directory = check_is_directory (monitor, file);
- }
-
- other_file_uri = g_file_get_uri (other_file);
- TRACKER_NOTE (MONITORS,
- g_message ("Received monitor event:%d (%s) for files '%s'->'%s'",
- event_type,
- monitor_event_to_string (event_type),
- file_uri,
- other_file_uri));
-
- if (is_directory &&
- (event_type == G_FILE_MONITOR_EVENT_RENAMED ||
- event_type == G_FILE_MONITOR_EVENT_MOVED_OUT) &&
- prev_event &&
- prev_event->event_type == G_FILE_MONITOR_EVENT_DELETED) {
- /* If a directory is moved, there is also an EVENT_DELETED
- * coming from the GFileMonitor on the folder itself (as the
- * folder being monitored no longer exists). We may receive
- * this event before this one, we should ensure it's cleared
- * out.
- */
- g_hash_table_remove (priv->thread.cached_events, file);
- }
- }
-
- /* Note that in any case we should be moving the monitors
- * here to the new place, as the new place may be ignored.
- * We should leave this to the upper layers. But one thing
- * we must do is actually CANCEL all these monitors. */
- if (is_directory &&
- (event_type == G_FILE_MONITOR_EVENT_RENAMED ||
- event_type == G_FILE_MONITOR_EVENT_MOVED_IN ||
- event_type == G_FILE_MONITOR_EVENT_DELETED)) {
- monitor_cancel_recursively (monitor, file);
- }
-
- switch (event_type) {
- case G_FILE_MONITOR_EVENT_CREATED:
- case G_FILE_MONITOR_EVENT_CHANGED:
- if (!priv->use_changed_event) {
- cache_event (monitor, file, event_type, is_directory);
- } else {
- queue_signal_for_event (monitor, event_type,
- is_directory, file, NULL);
- }
- break;
- case G_FILE_MONITOR_EVENT_DELETED:
- if (prev_event &&
- prev_event->event_type == G_FILE_MONITOR_EVENT_CREATED) {
- /* Consume both the cached CREATED event and this one */
- g_hash_table_remove (priv->thread.cached_events, file);
- break;
- }
-
- /* In any case, cached events are stale */
- g_hash_table_remove (priv->thread.cached_events, file);
-
- cache_event (monitor, file, event_type, is_directory);
- flush_event_later (monitor, file);
- break;
- case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
- queue_signal_for_event (monitor, event_type,
- is_directory, file, NULL);
- break;
- case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
- flush_cached_event (monitor, file);
- break;
- case G_FILE_MONITOR_EVENT_MOVED_IN:
- if (other_file) {
- /* Both MOVED_IN and MOVE_OUT are fine points to emit
- * ::item-moved when source/dest are known. We choose
- * to emit it here, and ignore the MOVE_OUT.
- */
- queue_signal_for_event (monitor,
- G_FILE_MONITOR_EVENT_MOVED,
- is_directory,
- other_file, file);
- } else {
- /* No known origin, treat as a new file */
- queue_signal_for_event (monitor,
- G_FILE_MONITOR_EVENT_CREATED,
- is_directory,
- file, NULL);
- }
- break;
- case G_FILE_MONITOR_EVENT_MOVED_OUT:
- if (!other_file) {
- /* No known destination. Treat as remove */
- queue_signal_for_event (monitor,
- G_FILE_MONITOR_EVENT_DELETED,
- is_directory,
- file, NULL);
- }
- break;
- case G_FILE_MONITOR_EVENT_RENAMED:
- queue_signal_for_event (monitor,
- G_FILE_MONITOR_EVENT_MOVED,
- is_directory, file, other_file);
- break;
- case G_FILE_MONITOR_EVENT_MOVED:
- g_warn_if_reached ();
- break;
- case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
- case G_FILE_MONITOR_EVENT_UNMOUNTED:
- break;
- }
-
- g_free (file_uri);
- g_free (other_file_uri);
-}
-
-static GFileMonitor *
-directory_monitor_new (TrackerMonitor *monitor,
- GFile *file)
-{
- GFileMonitor *file_monitor;
- GError *error = NULL;
-
- file_monitor = g_file_monitor_directory (file,
- G_FILE_MONITOR_WATCH_MOVES,
- NULL,
- &error);
-
- if (error) {
- gchar *uri;
-
- uri = g_file_get_uri (file);
- g_warning ("Could not add monitor for path:'%s', %s",
- uri, error->message);
-
- g_error_free (error);
- g_free (uri);
-
- return NULL;
- }
-
- g_signal_connect (file_monitor, "changed",
- G_CALLBACK (monitor_event_cb),
- monitor);
-
- return file_monitor;
-}
-
-static void
-directory_monitor_cancel (GFileMonitor *monitor)
-{
- if (monitor) {
- g_file_monitor_cancel (G_FILE_MONITOR (monitor));
- g_object_unref (monitor);
- }
-}
+ g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), FALSE);
+ g_return_val_if_fail (G_IS_FILE (old_file), FALSE);
+ g_return_val_if_fail (G_IS_FILE (new_file), FALSE);
-TrackerMonitor *
-tracker_monitor_new (void)
-{
- return g_object_new (TRACKER_TYPE_MONITOR, NULL);
+ return TRACKER_MONITOR_GET_CLASS (monitor)->move (monitor,
+ old_file,
+ new_file);
}
gboolean
tracker_monitor_get_enabled (TrackerMonitor *monitor)
{
- TrackerMonitorPrivate *priv;
+ gboolean enabled;
- g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), FALSE);
+ g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), 0);
- priv = tracker_monitor_get_instance_private (monitor);
+ g_object_get (monitor, "enabled", &enabled, NULL);
- return priv->enabled;
+ return enabled;
}
void
tracker_monitor_set_enabled (TrackerMonitor *monitor,
gboolean enabled)
{
- TrackerMonitorPrivate *priv;
- MonitorRequest *request;
-
g_return_if_fail (TRACKER_IS_MONITOR (monitor));
- priv = tracker_monitor_get_instance_private (monitor);
-
- /* Don't replace all monitors if we are already
- * enabled/disabled.
- */
- if (priv->enabled == enabled) {
- return;
- }
-
- priv->enabled = enabled;
- g_object_notify (G_OBJECT (monitor), "enabled");
-
- request = g_new0 (MonitorRequest, 1);
- request->monitor = monitor;
- request->files = g_hash_table_get_keys (priv->monitored_dirs);
- g_list_foreach (request->files, (GFunc) g_object_ref, NULL);
- request->type = enabled ? MONITOR_REQUEST_ADD : MONITOR_REQUEST_REMOVE;
-
- monitor_request_queue (monitor, request);
- block_for_requests (monitor);
+ g_object_set (monitor, "enabled", !!enabled, NULL);
}
gboolean
tracker_monitor_add (TrackerMonitor *monitor,
GFile *file)
{
- TrackerMonitorPrivate *priv;
- gchar *uri;
-
g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), FALSE);
g_return_val_if_fail (G_IS_FILE (file), FALSE);
- priv = tracker_monitor_get_instance_private (monitor);
-
- if (g_hash_table_lookup (priv->monitored_dirs, file)) {
- return TRUE;
- }
-
- /* Cap the number of monitors */
- if (g_hash_table_size (priv->monitored_dirs) >= priv->monitor_limit) {
- priv->monitors_ignored++;
-
- if (!priv->monitor_limit_warned) {
- g_warning ("The maximum number of monitors to set (%d) "
- "has been reached, not adding any new ones",
- priv->monitor_limit);
- priv->monitor_limit_warned = TRUE;
- }
-
- return FALSE;
- }
-
- uri = g_file_get_uri (file);
-
- if (priv->enabled) {
- /* We don't check if a file exists or not since we might want
- * to monitor locations which don't exist yet.
- *
- * Also, we assume ALL paths passed are directories.
- */
- MonitorRequest *request;
-
- request = g_new0 (MonitorRequest, 1);
- request->monitor = monitor;
- request->files = g_list_prepend (NULL, g_object_ref (file));
- request->type = MONITOR_REQUEST_ADD;
-
- monitor_request_queue (monitor, request);
- block_for_requests (monitor);
- }
-
- g_hash_table_add (priv->monitored_dirs, g_object_ref (file));
-
- TRACKER_NOTE (MONITORS, g_message ("Added monitor for path:'%s', total monitors:%d",
- uri,
- g_hash_table_size (priv->monitored_dirs)));
-
- g_free (uri);
-
- return TRUE;
+ return TRACKER_MONITOR_GET_CLASS (monitor)->add (monitor, file);
}
gboolean
tracker_monitor_remove (TrackerMonitor *monitor,
GFile *file)
{
- TrackerMonitorPrivate *priv;
- gboolean removed;
-
g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), FALSE);
g_return_val_if_fail (G_IS_FILE (file), FALSE);
- priv = tracker_monitor_get_instance_private (monitor);
- removed = g_hash_table_remove (priv->monitored_dirs, file);
-
- if (removed) {
- MonitorRequest *request;
- gchar *uri;
-
- request = g_new0 (MonitorRequest, 1);
- request->monitor = monitor;
- request->files = g_list_prepend (NULL, g_object_ref (file));
- request->type = MONITOR_REQUEST_REMOVE;
-
- monitor_request_queue (monitor, request);
- block_for_requests (monitor);
-
- uri = g_file_get_uri (file);
- TRACKER_NOTE (MONITORS, g_message ("Removed monitor for path:'%s', total monitors:%d",
- uri,
- g_hash_table_size (priv->monitored_dirs)));
-
- g_free (uri);
- }
-
- return removed;
-}
-
-/* If @is_strict is %TRUE, return %TRUE iff @file is a child of @prefix.
- * If @is_strict is %FALSE, additionally return %TRUE if @file equals @prefix.
- */
-static gboolean
-file_has_maybe_strict_prefix (GFile *file,
- GFile *prefix,
- gboolean is_strict)
-{
- return (g_file_has_prefix (file, prefix) ||
- (!is_strict && g_file_equal (file, prefix)));
-}
-
-static gboolean
-remove_recursively (TrackerMonitor *monitor,
- GFile *file,
- gboolean remove_top_level)
-{
- TrackerMonitorPrivate *priv;
- GHashTableIter iter;
- MonitorRequest *request;
- gpointer iter_file;
- guint items_removed = 0;
- gchar *uri;
-
- g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), FALSE);
- g_return_val_if_fail (G_IS_FILE (file), FALSE);
-
- priv = tracker_monitor_get_instance_private (monitor);
-
- request = g_new0 (MonitorRequest, 1);
- request->monitor = monitor;
- request->type = MONITOR_REQUEST_REMOVE;
-
- g_hash_table_iter_init (&iter, priv->monitored_dirs);
- while (g_hash_table_iter_next (&iter, &iter_file, NULL)) {
- if (!file_has_maybe_strict_prefix (iter_file, file,
- !remove_top_level)) {
- continue;
- }
-
- request->files = g_list_prepend (request->files, g_object_ref (file));
- g_hash_table_iter_remove (&iter);
- items_removed++;
- }
-
- uri = g_file_get_uri (file);
- TRACKER_NOTE (MONITORS,
- g_message ("Removed all monitors %srecursively for path:'%s', )"
- "total monitors:%d",
- !remove_top_level ? "(except top level) " : "",
- uri, g_hash_table_size (priv->monitored_dirs)));
- g_free (uri);
-
- monitor_request_queue (monitor, request);
- block_for_requests (monitor);
-
- if (items_removed > 0) {
- /* We reset this because now it is possible we have limit - 1 */
- priv->monitor_limit_warned = FALSE;
- return TRUE;
- }
-
- return FALSE;
+ return TRACKER_MONITOR_GET_CLASS (monitor)->remove (monitor,
+ file);
}
gboolean
tracker_monitor_remove_recursively (TrackerMonitor *monitor,
GFile *file)
{
- return remove_recursively (monitor, file, TRUE);
+ g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), FALSE);
+ g_return_val_if_fail (G_IS_FILE (file), FALSE);
+
+ return TRACKER_MONITOR_GET_CLASS (monitor)->remove_recursively (monitor,
+ file,
+ FALSE);
}
gboolean
tracker_monitor_remove_children_recursively (TrackerMonitor *monitor,
GFile *file)
{
- return remove_recursively (monitor, file, FALSE);
-}
-
-/* Runs in the monitor thread */
-static gboolean
-monitor_cancel_recursively (TrackerMonitor *monitor,
- GFile *file)
-{
- TrackerMonitorPrivate *priv;
- GHashTableIter iter;
- gpointer iter_file, iter_file_monitor;
- guint items_cancelled = 0;
-
- priv = tracker_monitor_get_instance_private (monitor);
-
- g_hash_table_iter_init (&iter, priv->thread.monitors);
- while (g_hash_table_iter_next (&iter, &iter_file, &iter_file_monitor)) {
- gchar *uri;
-
- if (!g_file_has_prefix (iter_file, file) &&
- !g_file_equal (iter_file, file)) {
- continue;
- }
-
- uri = g_file_get_uri (iter_file);
- g_file_monitor_cancel (G_FILE_MONITOR (iter_file_monitor));
- TRACKER_NOTE (MONITORS, g_message ("Cancelled monitor for path:'%s'", uri));
- g_free (uri);
-
- items_cancelled++;
- }
+ g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), FALSE);
+ g_return_val_if_fail (G_IS_FILE (file), FALSE);
- return items_cancelled > 0;
+ return TRACKER_MONITOR_GET_CLASS (monitor)->remove_recursively (monitor,
+ file,
+ TRUE);
}
gboolean
tracker_monitor_is_watched (TrackerMonitor *monitor,
GFile *file)
{
- TrackerMonitorPrivate *priv;
-
g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), FALSE);
g_return_val_if_fail (G_IS_FILE (file), FALSE);
- priv = tracker_monitor_get_instance_private (monitor);
-
- if (!priv->enabled)
- return FALSE;
-
- return g_hash_table_contains (priv->monitored_dirs, file);
+ return TRACKER_MONITOR_GET_CLASS (monitor)->is_watched (monitor,
+ file);
}
guint
tracker_monitor_get_count (TrackerMonitor *monitor)
{
- TrackerMonitorPrivate *priv;
+ guint count;
g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), 0);
- priv = tracker_monitor_get_instance_private (monitor);
+ g_object_get (monitor, "count", &count, NULL);
- return g_hash_table_size (priv->monitored_dirs);
+ return count;
}
guint
tracker_monitor_get_ignored (TrackerMonitor *monitor)
{
- TrackerMonitorPrivate *priv;
+ guint ignored;
g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), 0);
- priv = tracker_monitor_get_instance_private (monitor);
+ g_object_get (monitor, "ignored", &ignored, NULL);
- return priv->monitors_ignored;
+ return ignored;
}
guint
tracker_monitor_get_limit (TrackerMonitor *monitor)
{
- TrackerMonitorPrivate *priv;
+ guint limit;
g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), 0);
- priv = tracker_monitor_get_instance_private (monitor);
+ g_object_get (monitor, "limit", &limit, NULL);
- return priv->monitor_limit;
+ return limit;
+}
+
+void
+tracker_monitor_emit_created (TrackerMonitor *monitor,
+ GFile *file,
+ gboolean is_directory)
+
+{
+ g_signal_emit (monitor,
+ signals[ITEM_CREATED], 0,
+ file, is_directory);
+}
+
+void
+tracker_monitor_emit_updated (TrackerMonitor *monitor,
+ GFile *file,
+ gboolean is_directory)
+{
+ g_signal_emit (monitor,
+ signals[ITEM_UPDATED], 0,
+ file, is_directory);
+}
+
+void
+tracker_monitor_emit_attributes_updated (TrackerMonitor *monitor,
+ GFile *file,
+ gboolean is_directory)
+{
+ g_signal_emit (monitor,
+ signals[ITEM_ATTRIBUTE_UPDATED], 0,
+ file, is_directory);
+}
+
+void
+tracker_monitor_emit_deleted (TrackerMonitor *monitor,
+ GFile *file,
+ gboolean is_directory)
+{
+ g_signal_emit (monitor,
+ signals[ITEM_DELETED], 0,
+ file, is_directory);
+}
+
+void
+tracker_monitor_emit_moved (TrackerMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ gboolean is_directory)
+{
+ g_signal_emit (monitor,
+ signals[ITEM_MOVED], 0,
+ file, other_file,
+ is_directory, TRUE);
+}
+
+TrackerMonitor *
+tracker_monitor_new (GError **error)
+{
+ return g_initable_new (TRACKER_TYPE_MONITOR_GLIB,
+ NULL, error, NULL);
}
diff --git a/src/libtracker-miner/tracker-monitor.h b/src/libtracker-miner/tracker-monitor.h
index 132d7be64..68ddc7500 100644
--- a/src/libtracker-miner/tracker-monitor.h
+++ b/src/libtracker-miner/tracker-monitor.h
@@ -26,34 +26,32 @@
#include <glib-object.h>
#include <gio/gio.h>
-#include "tracker-indexing-tree.h"
G_BEGIN_DECLS
-#define TRACKER_TYPE_MONITOR (tracker_monitor_get_type ())
-#define TRACKER_MONITOR(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), TRACKER_TYPE_MONITOR,
TrackerMonitor))
-#define TRACKER_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TRACKER_TYPE_MONITOR,
TrackerMonitorClass))
-#define TRACKER_IS_MONITOR(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), TRACKER_TYPE_MONITOR))
-#define TRACKER_IS_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TRACKER_TYPE_MONITOR))
-#define TRACKER_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TRACKER_TYPE_MONITOR,
TrackerMonitorClass))
+#define TRACKER_TYPE_MONITOR (tracker_monitor_get_type ())
+G_DECLARE_DERIVABLE_TYPE (TrackerMonitor, tracker_monitor,
+ TRACKER, MONITOR,
+ GObject)
-typedef struct TrackerMonitor TrackerMonitor;
-typedef struct TrackerMonitorClass TrackerMonitorClass;
-
-struct TrackerMonitor {
- GObject parent;
-};
-
-struct TrackerMonitorClass {
+struct _TrackerMonitorClass {
GObjectClass parent;
+
+ gboolean (* add) (TrackerMonitor *monitor,
+ GFile *file);
+ gboolean (* remove) (TrackerMonitor *monitor,
+ GFile *file);
+ gboolean (* remove_recursively) (TrackerMonitor *monitor,
+ GFile *file,
+ gboolean only_children);
+ gboolean (* move) (TrackerMonitor *monitor,
+ GFile *src,
+ GFile *dst);
+ gboolean (* is_watched) (TrackerMonitor *monitor,
+ GFile *file);
};
GType tracker_monitor_get_type (void);
-TrackerMonitor *tracker_monitor_new (void);
-
-TrackerIndexingTree * tracker_monitor_get_indexing_tree (TrackerMonitor *monitor);
-void tracker_monitor_set_indexing_tree (TrackerMonitor *monitor,
- TrackerIndexingTree *tree);
gboolean tracker_monitor_get_enabled (TrackerMonitor *monitor);
void tracker_monitor_set_enabled (TrackerMonitor *monitor,
@@ -75,6 +73,8 @@ guint tracker_monitor_get_count (TrackerMonitor *monitor);
guint tracker_monitor_get_ignored (TrackerMonitor *monitor);
guint tracker_monitor_get_limit (TrackerMonitor *monitor);
+TrackerMonitor * tracker_monitor_new (GError **error);
+
G_END_DECLS
#endif /* __LIBTRACKER_MINER_MONITOR_H__ */
diff --git a/tests/libtracker-miner/tracker-monitor-test.c b/tests/libtracker-miner/tracker-monitor-test.c
index d5069f273..6894dc045 100644
--- a/tests/libtracker-miner/tracker-monitor-test.c
+++ b/tests/libtracker-miner/tracker-monitor-test.c
@@ -404,8 +404,7 @@ test_monitor_common_setup (TrackerMonitorTestFixture *fixture,
NULL);
/* Create and setup the tracker monitor */
- fixture->monitor = tracker_monitor_new ();
- g_initable_init (G_INITABLE (fixture->monitor), NULL, &error);
+ fixture->monitor = tracker_monitor_new (&error);
g_assert_no_error (error);
g_assert_true (fixture->monitor != NULL);
@@ -1281,8 +1280,7 @@ test_monitor_basic (void)
g_assert_true (G_IS_FILE (file_for_tmp));
/* Create a monitor */
- monitor = tracker_monitor_new ();
- g_initable_init (G_INITABLE (monitor), NULL, &error);
+ monitor = tracker_monitor_new (&error);
g_assert_no_error (error);
g_assert_true (monitor != NULL);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]