[tracker/libtracker-miner: 3/3] Raw port of handy code from tracker-miner-fs that will be needed for TrackerMinerCrawler.
- From: Carlos Garnacho <carlosg src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [tracker/libtracker-miner: 3/3] Raw port of handy code from tracker-miner-fs that will be needed for TrackerMinerCrawler.
- Date: Fri, 31 Jul 2009 10:05:34 +0000 (UTC)
commit 99e6630f704b5efb56d3144ca98c5bb42bc5e395
Author: Carlos Garnacho <carlos lanedo com>
Date: Thu Jul 30 18:13:15 2009 +0200
Raw port of handy code from tracker-miner-fs that will be needed for TrackerMinerCrawler.
Things are broken, and dependency on config, modules config, and possibly
status, should be removed all along the code.
src/libtracker-miner/Makefile.am | 33 +-
src/libtracker-miner/tracker-config.c | 1065 ++++++++++++++++
src/libtracker-miner/tracker-config.h | 119 ++
src/libtracker-miner/tracker-crawler.c | 1236 ++++++++++++++++++
src/libtracker-miner/tracker-crawler.h | 70 +
src/libtracker-miner/tracker-marshal.list | 8 +
src/libtracker-miner/tracker-miner-crawler.c | 23 +-
src/libtracker-miner/tracker-monitor.c | 1632 +++++++++++++++++++++++
src/libtracker-miner/tracker-monitor.h | 77 ++
src/libtracker-miner/tracker-processor.c | 1774 ++++++++++++++++++++++++++
src/libtracker-miner/tracker-processor.h | 64 +
src/libtracker-miner/tracker-status.c | 1097 ++++++++++++++++
src/libtracker-miner/tracker-status.h | 96 ++
src/libtracker-miner/tracker-utils.c | 43 +
src/libtracker-miner/tracker-utils.h | 31 +
15 files changed, 7365 insertions(+), 3 deletions(-)
---
diff --git a/src/libtracker-miner/Makefile.am b/src/libtracker-miner/Makefile.am
index 08b181c..15a56f8 100644
--- a/src/libtracker-miner/Makefile.am
+++ b/src/libtracker-miner/Makefile.am
@@ -22,13 +22,27 @@ libtracker_minerincludedir=$(includedir)/tracker-$(TRACKER_API_VERSION)/libtrack
libtracker_miner_LTLIBRARIES = libtracker-miner.la
libtracker_miner_la_SOURCES = \
+ tracker-config.c \
+ tracker-config.h \
+ tracker-crawler.c \
+ tracker-crawler.h \
tracker-dbus.c \
tracker-dbus.h \
+ tracker-marshal.c \
+ tracker-marshal.h \
tracker-miner-dbus.h \
tracker-miner.c \
tracker-miner.h \
tracker-miner-crawler.c \
- tracker-miner-crawler.h
+ tracker-miner-crawler.h \
+ tracker-monitor.c \
+ tracker-monitor.h \
+ tracker-processor.c \
+ tracker-processor.h \
+ tracker-status.c \
+ tracker-status.h \
+ tracker-utils.c \
+ tracker-utils.h
libtracker_minerinclude_HEADERS = \
tracker-miner.h \
@@ -39,7 +53,9 @@ libtracker_miner_la_LDFLAGS = \
libtracker_miner_la_LIBADD = \
$(top_builddir)/src/libstemmer/libstemmer.la \
+ $(top_builddir)/src/libtracker-common/libtracker-common.la \
$(top_builddir)/src/libtracker/libtrackerclient- TRACKER_API_VERSION@.la \
+ $(top_builddir)/src/libinotify/libinotify.la \
$(HAL_LIBS) \
$(DEVKIT_POWER_LIBS) \
$(DBUS_LIBS) \
@@ -50,15 +66,28 @@ libtracker_miner_la_LIBADD = \
$(GLIB2_LIBS) \
$(GDKPIXBUF_LIBS)
+tracker-marshal.h: tracker-marshal.list
+ $(GLIB_GENMARSHAL) $< --prefix=tracker_marshal --header > $@
+
+tracker-marshal.c: tracker-marshal.list
+ (echo "#include \"tracker-marshal.h\""; \
+ $(GLIB_GENMARSHAL) $< --prefix=tracker_marshal --body) > $@
+
+marshal_sources = \
+ tracker-marshal.c \
+ tracker-marshal.h
+
dbus_sources = tracker-miner-glue.h
%-glue.h: $(top_srcdir)/data/dbus/%.xml
$(DBUSBINDINGTOOL) --mode=glib-server --output=$@ --prefix=$(subst -,_,$*) $^
-BUILT_SOURCES = $(dbus_sources)
+BUILT_SOURCES = $(dbus_sources) $(marshal_sources)
CLEANFILES = $(BUILT_SOURCES)
+EXTRA_DIST = tracker-marshal.list
+
libexec_PROGRAMS = tracker-miner-test
tracker_miner_test_SOURCES = \
diff --git a/src/libtracker-miner/tracker-config.c b/src/libtracker-miner/tracker-config.c
new file mode 100644
index 0000000..00d17c7
--- /dev/null
+++ b/src/libtracker-miner/tracker-config.c
@@ -0,0 +1,1065 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009, Nokia (urho konttori nokia com)
+ *
+ * This library 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 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libtracker-common/tracker-keyfile-object.h>
+#include <libtracker-common/tracker-file-utils.h>
+#include <libtracker-common/tracker-type-utils.h>
+
+#include "tracker-config.h"
+
+#define TRACKER_CONFIG_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TRACKER_TYPE_CONFIG, TrackerConfigPrivate))
+
+/* GKeyFile defines */
+#define GROUP_GENERAL "General"
+#define GROUP_MONITORS "Monitors"
+#define GROUP_INDEXING "Indexing"
+
+/* Default values */
+#define DEFAULT_VERBOSITY 0
+#define DEFAULT_INITIAL_SLEEP 15 /* 0->1000 */
+#define DEFAULT_ENABLE_WATCHES TRUE
+#define DEFAULT_THROTTLE 0 /* 0->20 */
+#define DEFAULT_ENABLE_THUMBNAILS TRUE
+#define DEFAULT_DISABLE_INDEXING_ON_BATTERY TRUE
+#define DEFAULT_DISABLE_INDEXING_ON_BATTERY_INIT FALSE
+#define DEFAULT_INDEX_MOUNTED_DIRECTORIES TRUE
+#define DEFAULT_INDEX_REMOVABLE_DEVICES TRUE
+#define DEFAULT_LOW_DISK_SPACE_LIMIT 1 /* 0->100 / -1 */
+
+typedef struct {
+ /* General */
+ gint verbosity;
+ gint initial_sleep;
+
+ /* Watches */
+ GSList *watch_directory_roots;
+ GSList *crawl_directory_roots;
+ GSList *no_watch_directory_roots;
+ gboolean enable_watches;
+
+ /* Indexing */
+ gint throttle;
+ gboolean enable_thumbnails;
+ GSList *disabled_modules;
+
+ gboolean disable_indexing_on_battery;
+ gboolean disable_indexing_on_battery_init;
+ gint low_disk_space_limit;
+ gboolean index_mounted_directories;
+ gboolean index_removable_devices;
+} TrackerConfigPrivate;
+
+typedef struct {
+ GType type;
+ gchar *property;
+ gchar *group;
+ gchar *key;
+} ObjectToKeyFile;
+
+static void config_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void config_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void config_finalize (GObject *object);
+static void config_constructed (GObject *object);
+static void config_load (TrackerConfig *config);
+static gboolean config_save (TrackerConfig *config);
+static void config_create_with_defaults (TrackerConfig *config,
+ GKeyFile *key_file,
+ gboolean overwrite);
+
+enum {
+ PROP_0,
+
+ /* General */
+ PROP_VERBOSITY,
+ PROP_INITIAL_SLEEP,
+
+ /* Watches */
+ PROP_ENABLE_WATCHES,
+ PROP_WATCH_DIRECTORY_ROOTS,
+ PROP_CRAWL_DIRECTORY_ROOTS,
+ PROP_NO_WATCH_DIRECTORY_ROOTS,
+
+ /* Indexing */
+ PROP_THROTTLE,
+ PROP_ENABLE_THUMBNAILS,
+ PROP_DISABLED_MODULES,
+ PROP_DISABLE_INDEXING_ON_BATTERY,
+ PROP_DISABLE_INDEXING_ON_BATTERY_INIT,
+ PROP_LOW_DISK_SPACE_LIMIT,
+ PROP_INDEX_MOUNTED_DIRECTORIES,
+ PROP_INDEX_REMOVABLE_DEVICES,
+};
+
+
+static ObjectToKeyFile conversions[] = {
+ { G_TYPE_INT, "verbosity", GROUP_GENERAL, "Verbosity" },
+ { G_TYPE_INT, "initial-sleep", GROUP_GENERAL, "InitialSleep" },
+ { G_TYPE_BOOLEAN, "enable-watches", GROUP_MONITORS, "EnableWatches" },
+ { G_TYPE_POINTER, "watch-directory-roots", GROUP_MONITORS, "WatchDirectoryRoots" },
+ { G_TYPE_POINTER, "crawl-directory-roots", GROUP_MONITORS, "CrawlDirectoryRoots" },
+ { G_TYPE_POINTER, "no-watch-directory-roots", GROUP_MONITORS, "NoWatchDirectory" },
+ { G_TYPE_INT, "throttle", GROUP_INDEXING, "Throttle" },
+ { G_TYPE_BOOLEAN, "enable-thumbnails", GROUP_INDEXING, "EnableThumbnails" },
+ { G_TYPE_POINTER, "disabled-modules", GROUP_INDEXING, "DisabledModules" },
+ { G_TYPE_BOOLEAN, "disable-indexing-on-battery", GROUP_INDEXING, "BatteryIndex" },
+ { G_TYPE_BOOLEAN, "disable-indexing-on-battery-init", GROUP_INDEXING, "BatteryIndexInitial" },
+ { G_TYPE_INT, "low-disk-space-limit", GROUP_INDEXING, "LowDiskSpaceLimit" },
+ { G_TYPE_BOOLEAN, "index-mounted-directories", GROUP_INDEXING, "IndexMountedDirectories" },
+ { G_TYPE_BOOLEAN, "index-removable-devices", GROUP_INDEXING, "IndexRemovableMedia" },
+};
+
+G_DEFINE_TYPE (TrackerConfig, tracker_config, TRACKER_TYPE_CONFIG_FILE);
+
+static void
+tracker_config_class_init (TrackerConfigClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->set_property = config_set_property;
+ object_class->get_property = config_get_property;
+ object_class->finalize = config_finalize;
+ object_class->constructed = config_constructed;
+
+ /* General */
+ g_object_class_install_property (object_class,
+ PROP_VERBOSITY,
+ g_param_spec_int ("verbosity",
+ "Log verbosity",
+ " Log verbosity (0=errors, 1=minimal, 2=detailed, 3=debug)",
+ 0,
+ 3,
+ DEFAULT_VERBOSITY,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class,
+ PROP_INITIAL_SLEEP,
+ g_param_spec_int ("initial-sleep",
+ "Initial sleep",
+ " Time in seconds before crawling filesystem (0->1000)",
+ 0,
+ 1000,
+ DEFAULT_INITIAL_SLEEP,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ /* Monitors */
+ g_object_class_install_property (object_class,
+ PROP_ENABLE_WATCHES,
+ g_param_spec_boolean ("enable-watches",
+ "Enable watches",
+ " Set to false to completely disable any watching",
+ DEFAULT_ENABLE_WATCHES,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class,
+ PROP_WATCH_DIRECTORY_ROOTS,
+ g_param_spec_pointer ("watch-directory-roots",
+ "Watched directory roots",
+ " List of directory roots to index and watch (separator=;)",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class,
+ PROP_CRAWL_DIRECTORY_ROOTS,
+ g_param_spec_pointer ("crawl-directory-roots",
+ "Crawl directory roots",
+ " List of directory roots to index but NOT watch (separator=;)",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class,
+ PROP_NO_WATCH_DIRECTORY_ROOTS,
+ g_param_spec_pointer ("no-watch-directory-roots",
+ "Not watched directory roots",
+ " List of directory roots NOT to index and NOT to watch (separator=;)",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ /* Indexing */
+ g_object_class_install_property (object_class,
+ PROP_THROTTLE,
+ g_param_spec_int ("throttle",
+ "Throttle",
+ " Sets the indexing speed (0->20, where 20=slowest speed)",
+ 0,
+ 20,
+ DEFAULT_THROTTLE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class,
+ PROP_ENABLE_THUMBNAILS,
+ g_param_spec_boolean ("enable-thumbnails",
+ "Enable thumbnails",
+ " Set to false to completely disable thumbnail generation",
+ DEFAULT_ENABLE_THUMBNAILS,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class,
+ PROP_DISABLED_MODULES,
+ g_param_spec_pointer ("disabled-modules",
+ "Disabled modules",
+ " List of disabled modules (separator=;)\n"
+ " The modules that are indexed are kept in $prefix/lib/tracker/indexer-modules",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ ;
+ g_object_class_install_property (object_class,
+ PROP_DISABLE_INDEXING_ON_BATTERY,
+ g_param_spec_boolean ("disable-indexing-on-battery",
+ "Disable indexing on battery",
+ " Set to true to disable indexing when running on battery",
+ DEFAULT_DISABLE_INDEXING_ON_BATTERY,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class,
+ PROP_DISABLE_INDEXING_ON_BATTERY_INIT,
+ g_param_spec_boolean ("disable-indexing-on-battery-init",
+ "Disable indexing on battery",
+ " Set to true to disable initial indexing when running on battery",
+ DEFAULT_DISABLE_INDEXING_ON_BATTERY_INIT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class,
+ PROP_LOW_DISK_SPACE_LIMIT,
+ g_param_spec_int ("low-disk-space-limit",
+ "Low disk space limit",
+ " Pause indexer when disk space is <= this value\n"
+ " (0->100, value is in % of $HOME file system, -1=disable pausing)",
+ -1,
+ 100,
+ DEFAULT_LOW_DISK_SPACE_LIMIT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class,
+ PROP_INDEX_MOUNTED_DIRECTORIES,
+ g_param_spec_boolean ("index-mounted-directories",
+ "Index mounted directories",
+ " Set to true to enable traversing mounted directories on other file systems\n"
+ " (this excludes removable devices)",
+ DEFAULT_INDEX_MOUNTED_DIRECTORIES,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class,
+ PROP_INDEX_REMOVABLE_DEVICES,
+ g_param_spec_boolean ("index-removable-devices",
+ "index removable devices",
+ " Set to true to enable traversing mounted directories for removable devices",
+ DEFAULT_INDEX_REMOVABLE_DEVICES,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_type_class_add_private (object_class, sizeof (TrackerConfigPrivate));
+}
+
+static void
+tracker_config_init (TrackerConfig *object)
+{
+}
+
+static void
+config_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (param_id) {
+ /* General */
+ case PROP_VERBOSITY:
+ tracker_config_set_verbosity (TRACKER_CONFIG (object),
+ g_value_get_int (value));
+ break;
+ case PROP_INITIAL_SLEEP:
+ tracker_config_set_initial_sleep (TRACKER_CONFIG (object),
+ g_value_get_int (value));
+ break;
+
+ /* Watches */
+ case PROP_WATCH_DIRECTORY_ROOTS:
+ tracker_config_set_watch_directory_roots (TRACKER_CONFIG (object),
+ g_value_get_pointer (value));
+ break;
+
+ case PROP_CRAWL_DIRECTORY_ROOTS:
+ tracker_config_set_crawl_directory_roots (TRACKER_CONFIG (object),
+ g_value_get_pointer (value));
+ break;
+ case PROP_NO_WATCH_DIRECTORY_ROOTS:
+ tracker_config_set_no_watch_directory_roots (TRACKER_CONFIG (object),
+ g_value_get_pointer (value));
+ break;
+ case PROP_ENABLE_WATCHES:
+ tracker_config_set_enable_watches (TRACKER_CONFIG (object),
+ g_value_get_boolean (value));
+ break;
+
+ /* Indexing */
+ case PROP_THROTTLE:
+ tracker_config_set_throttle (TRACKER_CONFIG (object),
+ g_value_get_int (value));
+ break;
+ case PROP_ENABLE_THUMBNAILS:
+ tracker_config_set_enable_thumbnails (TRACKER_CONFIG (object),
+ g_value_get_boolean (value));
+ break;
+ case PROP_DISABLED_MODULES:
+ tracker_config_set_disabled_modules (TRACKER_CONFIG (object),
+ g_value_get_pointer (value));
+ break;
+ case PROP_DISABLE_INDEXING_ON_BATTERY:
+ tracker_config_set_disable_indexing_on_battery (TRACKER_CONFIG (object),
+ g_value_get_boolean (value));
+ break;
+ case PROP_DISABLE_INDEXING_ON_BATTERY_INIT:
+ tracker_config_set_disable_indexing_on_battery_init (TRACKER_CONFIG (object),
+ g_value_get_boolean (value));
+ break;
+ case PROP_LOW_DISK_SPACE_LIMIT:
+ tracker_config_set_low_disk_space_limit (TRACKER_CONFIG (object),
+ g_value_get_int (value));
+ break;
+ case PROP_INDEX_MOUNTED_DIRECTORIES:
+ tracker_config_set_index_mounted_directories (TRACKER_CONFIG (object),
+ g_value_get_boolean (value));
+ break;
+ case PROP_INDEX_REMOVABLE_DEVICES:
+ tracker_config_set_index_removable_devices (TRACKER_CONFIG (object),
+ g_value_get_boolean (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ };
+}
+
+static void
+config_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ TrackerConfigPrivate *priv;
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (object);
+
+ switch (param_id) {
+ /* General */
+ case PROP_VERBOSITY:
+ g_value_set_int (value, priv->verbosity);
+ break;
+ case PROP_INITIAL_SLEEP:
+ g_value_set_int (value, priv->initial_sleep);
+ break;
+
+ /* Watches */
+ case PROP_WATCH_DIRECTORY_ROOTS:
+ g_value_set_pointer (value, priv->watch_directory_roots);
+ break;
+ case PROP_CRAWL_DIRECTORY_ROOTS:
+ g_value_set_pointer (value, priv->crawl_directory_roots);
+ break;
+ case PROP_NO_WATCH_DIRECTORY_ROOTS:
+ g_value_set_pointer (value, priv->no_watch_directory_roots);
+ break;
+ case PROP_ENABLE_WATCHES:
+ g_value_set_boolean (value, priv->enable_watches);
+ break;
+
+ /* Indexing */
+ case PROP_THROTTLE:
+ g_value_set_int (value, priv->throttle);
+ break;
+ case PROP_ENABLE_THUMBNAILS:
+ g_value_set_boolean (value, priv->enable_thumbnails);
+ break;
+ case PROP_DISABLED_MODULES:
+ g_value_set_pointer (value, priv->disabled_modules);
+ break;
+ case PROP_DISABLE_INDEXING_ON_BATTERY:
+ g_value_set_boolean (value, priv->disable_indexing_on_battery);
+ break;
+ case PROP_DISABLE_INDEXING_ON_BATTERY_INIT:
+ g_value_set_boolean (value, priv->disable_indexing_on_battery_init);
+ break;
+ case PROP_LOW_DISK_SPACE_LIMIT:
+ g_value_set_int (value, priv->low_disk_space_limit);
+ break;
+ case PROP_INDEX_MOUNTED_DIRECTORIES:
+ g_value_set_boolean (value, priv->index_mounted_directories);
+ break;
+ case PROP_INDEX_REMOVABLE_DEVICES:
+ g_value_set_boolean (value, priv->index_removable_devices);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ };
+}
+
+static void
+config_finalize (GObject *object)
+{
+ TrackerConfigPrivate *priv;
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (object);
+
+ g_slist_foreach (priv->watch_directory_roots, (GFunc) g_free, NULL);
+ g_slist_free (priv->watch_directory_roots);
+
+ g_slist_foreach (priv->crawl_directory_roots, (GFunc) g_free, NULL);
+ g_slist_free (priv->crawl_directory_roots);
+
+ g_slist_foreach (priv->no_watch_directory_roots, (GFunc) g_free, NULL);
+ g_slist_free (priv->no_watch_directory_roots);
+
+ g_slist_foreach (priv->disabled_modules, (GFunc) g_free, NULL);
+ g_slist_free (priv->disabled_modules);
+
+ (G_OBJECT_CLASS (tracker_config_parent_class)->finalize) (object);
+}
+
+static void
+config_constructed (GObject *object)
+{
+ (G_OBJECT_CLASS (tracker_config_parent_class)->constructed) (object);
+
+ config_load (TRACKER_CONFIG (object));
+}
+
+static void
+config_create_with_defaults (TrackerConfig *config,
+ GKeyFile *key_file,
+ gboolean overwrite)
+{
+ gint i;
+
+ g_message ("Loading defaults into GKeyFile...");
+
+ for (i = 0; i < G_N_ELEMENTS (conversions); i++) {
+ gboolean has_key;
+
+ has_key = g_key_file_has_key (key_file,
+ conversions[i].group,
+ conversions[i].key,
+ NULL);
+ if (!overwrite && has_key) {
+ continue;
+ }
+
+ switch (conversions[i].type) {
+ case G_TYPE_INT:
+ g_key_file_set_integer (key_file,
+ conversions[i].group,
+ conversions[i].key,
+ tracker_keyfile_object_default_int (config,
+ conversions[i].property));
+ break;
+
+ case G_TYPE_BOOLEAN:
+ g_key_file_set_boolean (key_file,
+ conversions[i].group,
+ conversions[i].key,
+ tracker_keyfile_object_default_boolean (config,
+ conversions[i].property));
+ break;
+
+ case G_TYPE_POINTER:
+ /* Special case string lists */
+ if (g_strcmp0 (conversions[i].property, "watch-directory-roots") == 0) {
+ const gchar *string_list[] = { NULL, NULL };
+
+ string_list[0] = g_get_home_dir ();
+
+ g_key_file_set_string_list (key_file,
+ conversions[i].group,
+ conversions[i].key,
+ string_list,
+ G_N_ELEMENTS (string_list));
+ } else {
+ const gchar *string_list[] = { NULL };
+
+ g_key_file_set_string_list (key_file,
+ conversions[i].group,
+ conversions[i].key,
+ string_list,
+ G_N_ELEMENTS (string_list));
+ }
+
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+
+ g_key_file_set_comment (key_file,
+ conversions[i].group,
+ conversions[i].key,
+ tracker_keyfile_object_blurb (config,
+ conversions[i].property),
+ NULL);
+ }
+}
+
+static void
+config_load (TrackerConfig *config)
+{
+ TrackerConfigFile *file;
+ gint i;
+
+ file = TRACKER_CONFIG_FILE (config);
+ config_create_with_defaults (config, file->key_file, FALSE);
+
+ if (!file->file_exists) {
+ tracker_config_file_save (file);
+ }
+
+ for (i = 0; i < G_N_ELEMENTS (conversions); i++) {
+ gboolean has_key;
+ gboolean is_directory_list;
+
+ has_key = g_key_file_has_key (file->key_file,
+ conversions[i].group,
+ conversions[i].key,
+ NULL);
+
+ switch (conversions[i].type) {
+ case G_TYPE_INT:
+ tracker_keyfile_object_load_int (G_OBJECT (file),
+ conversions[i].property,
+ file->key_file,
+ conversions[i].group,
+ conversions[i].key);
+ break;
+
+ case G_TYPE_BOOLEAN:
+ tracker_keyfile_object_load_boolean (G_OBJECT (file),
+ conversions[i].property,
+ file->key_file,
+ conversions[i].group,
+ conversions[i].key);
+ break;
+
+ case G_TYPE_POINTER:
+ if (g_strcmp0 (conversions[i].property, "disabled-modules") == 0) {
+ is_directory_list = FALSE;
+ } else {
+ is_directory_list = TRUE;
+ }
+
+ tracker_keyfile_object_load_string_list (G_OBJECT (file),
+ conversions[i].property,
+ file->key_file,
+ conversions[i].group,
+ conversions[i].key,
+ is_directory_list);
+ break;
+ }
+ }
+}
+
+static gboolean
+config_save (TrackerConfig *config)
+{
+ TrackerConfigFile *file;
+ gint i;
+
+ file = TRACKER_CONFIG_FILE (config);
+
+ if (!file->key_file) {
+ g_critical ("Could not save config, GKeyFile was NULL, has the config been loaded?");
+
+ return FALSE;
+ }
+
+ g_message ("Setting details to GKeyFile object...");
+
+ for (i = 0; i < G_N_ELEMENTS (conversions); i++) {
+ switch (conversions[i].type) {
+ case G_TYPE_INT:
+ tracker_keyfile_object_save_int (file,
+ conversions[i].property,
+ file->key_file,
+ conversions[i].group,
+ conversions[i].key);
+ break;
+
+ case G_TYPE_BOOLEAN:
+ tracker_keyfile_object_save_boolean (file,
+ conversions[i].property,
+ file->key_file,
+ conversions[i].group,
+ conversions[i].key);
+ break;
+
+ case G_TYPE_POINTER:
+ tracker_keyfile_object_save_string_list (file,
+ conversions[i].property,
+ file->key_file,
+ conversions[i].group,
+ conversions[i].key);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ }
+
+ return tracker_config_file_save (file);
+}
+
+TrackerConfig *
+tracker_config_new (void)
+{
+ return g_object_new (TRACKER_TYPE_CONFIG,
+ "domain", "foo",
+ NULL);
+}
+
+gboolean
+tracker_config_save (TrackerConfig *config)
+{
+ g_return_val_if_fail (TRACKER_IS_CONFIG (config), FALSE);
+
+ return config_save (config);
+}
+
+gint
+tracker_config_get_verbosity (TrackerConfig *config)
+{
+ TrackerConfigPrivate *priv;
+
+ g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_VERBOSITY);
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+ return priv->verbosity;
+}
+
+gint
+tracker_config_get_initial_sleep (TrackerConfig *config)
+{
+ TrackerConfigPrivate *priv;
+
+ g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_INITIAL_SLEEP);
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+ return priv->initial_sleep;
+}
+
+GSList *
+tracker_config_get_watch_directory_roots (TrackerConfig *config)
+{
+ TrackerConfigPrivate *priv;
+
+ g_return_val_if_fail (TRACKER_IS_CONFIG (config), NULL);
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+ return priv->watch_directory_roots;
+}
+
+GSList *
+tracker_config_get_crawl_directory_roots (TrackerConfig *config)
+{
+ TrackerConfigPrivate *priv;
+
+ g_return_val_if_fail (TRACKER_IS_CONFIG (config), NULL);
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+ return priv->crawl_directory_roots;
+}
+
+GSList *
+tracker_config_get_no_watch_directory_roots (TrackerConfig *config)
+{
+ TrackerConfigPrivate *priv;
+
+ g_return_val_if_fail (TRACKER_IS_CONFIG (config), NULL);
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+ return priv->no_watch_directory_roots;
+}
+
+gboolean
+tracker_config_get_enable_watches (TrackerConfig *config)
+{
+ TrackerConfigPrivate *priv;
+
+ g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_ENABLE_WATCHES);
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+ return priv->enable_watches;
+}
+
+gint
+tracker_config_get_throttle (TrackerConfig *config)
+{
+ TrackerConfigPrivate *priv;
+
+ g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_THROTTLE);
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+ return priv->throttle;
+}
+
+gboolean
+tracker_config_get_enable_thumbnails (TrackerConfig *config)
+{
+ TrackerConfigPrivate *priv;
+
+ g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_ENABLE_THUMBNAILS);
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+ return priv->enable_thumbnails;
+}
+
+GSList *
+tracker_config_get_disabled_modules (TrackerConfig *config)
+{
+ TrackerConfigPrivate *priv;
+
+ g_return_val_if_fail (TRACKER_IS_CONFIG (config), NULL);
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+ return priv->disabled_modules;
+}
+
+gboolean
+tracker_config_get_disable_indexing_on_battery (TrackerConfig *config)
+{
+ TrackerConfigPrivate *priv;
+
+ g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_DISABLE_INDEXING_ON_BATTERY);
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+ return priv->disable_indexing_on_battery;
+}
+
+gboolean
+tracker_config_get_disable_indexing_on_battery_init (TrackerConfig *config)
+{
+ TrackerConfigPrivate *priv;
+
+ g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_DISABLE_INDEXING_ON_BATTERY_INIT);
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+ return priv->disable_indexing_on_battery_init;
+}
+
+gint
+tracker_config_get_low_disk_space_limit (TrackerConfig *config)
+{
+ TrackerConfigPrivate *priv;
+
+ g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_LOW_DISK_SPACE_LIMIT);
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+ return priv->low_disk_space_limit;
+}
+
+gboolean
+tracker_config_get_index_mounted_directories (TrackerConfig *config)
+{
+ TrackerConfigPrivate *priv;
+
+ g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_INDEX_MOUNTED_DIRECTORIES);
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+ return priv->index_mounted_directories;
+}
+
+gboolean
+tracker_config_get_index_removable_devices (TrackerConfig *config)
+{
+ TrackerConfigPrivate *priv;
+
+ g_return_val_if_fail (TRACKER_IS_CONFIG (config), DEFAULT_INDEX_REMOVABLE_DEVICES);
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+ return priv->index_removable_devices;
+}
+
+void
+tracker_config_set_verbosity (TrackerConfig *config,
+ gint value)
+{
+ TrackerConfigPrivate *priv;
+
+ g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+ if (!tracker_keyfile_object_validate_int (config, "verbosity", value)) {
+ return;
+ }
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+ priv->verbosity = value;
+ g_object_notify (G_OBJECT (config), "verbosity");
+}
+
+void
+tracker_config_set_initial_sleep (TrackerConfig *config,
+ gint value)
+{
+ TrackerConfigPrivate *priv;
+
+ g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+ if (!tracker_keyfile_object_validate_int (config, "initial-sleep", value)) {
+ return;
+ }
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+ priv->initial_sleep = value;
+ g_object_notify (G_OBJECT (config), "initial-sleep");
+}
+
+void
+tracker_config_set_enable_watches (TrackerConfig *config,
+ gboolean value)
+{
+ TrackerConfigPrivate *priv;
+
+ g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+ priv->enable_watches = value;
+ g_object_notify (G_OBJECT (config), "enable-watches");
+}
+
+void
+tracker_config_set_throttle (TrackerConfig *config,
+ gint value)
+{
+ TrackerConfigPrivate *priv;
+
+ g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+ if (!tracker_keyfile_object_validate_int (config, "throttle", value)) {
+ return;
+ }
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+ priv->throttle = value;
+ g_object_notify (G_OBJECT (config), "throttle");
+}
+
+void
+tracker_config_set_enable_thumbnails (TrackerConfig *config,
+ gboolean value)
+{
+ TrackerConfigPrivate *priv;
+
+ g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+ priv->enable_thumbnails = value;
+ g_object_notify (G_OBJECT (config), "enable-thumbnails");
+}
+
+void
+tracker_config_set_disable_indexing_on_battery (TrackerConfig *config,
+ gboolean value)
+{
+ TrackerConfigPrivate *priv;
+
+ g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+ priv->disable_indexing_on_battery = value;
+ g_object_notify (G_OBJECT (config), "disable-indexing-on-battery");
+}
+
+void
+tracker_config_set_disable_indexing_on_battery_init (TrackerConfig *config,
+ gboolean value)
+{
+ TrackerConfigPrivate *priv;
+
+ g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+ priv->disable_indexing_on_battery_init = value;
+ g_object_notify (G_OBJECT (config), "disable-indexing-on-battery-init");
+}
+
+void
+tracker_config_set_low_disk_space_limit (TrackerConfig *config,
+ gint value)
+{
+ TrackerConfigPrivate *priv;
+
+ g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+ if (!tracker_keyfile_object_validate_int (config, "low-disk-space-limit", value)) {
+ return;
+ }
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+ priv->low_disk_space_limit = value;
+ g_object_notify (G_OBJECT (config), "low-disk-space-limit");
+}
+
+void
+tracker_config_set_index_mounted_directories (TrackerConfig *config,
+ gboolean value)
+{
+ TrackerConfigPrivate *priv;
+
+ g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+ priv->index_mounted_directories = value;
+ g_object_notify (G_OBJECT (config), "index-mounted-directories");
+}
+
+void
+tracker_config_set_index_removable_devices (TrackerConfig *config,
+ gboolean value)
+{
+ TrackerConfigPrivate *priv;
+
+ g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+ priv->index_removable_devices = value;
+ g_object_notify (G_OBJECT (config), "index-removable-devices");
+}
+
+void
+tracker_config_set_watch_directory_roots (TrackerConfig *config,
+ GSList *roots)
+{
+ TrackerConfigPrivate *priv;
+ GSList *l;
+
+ g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+ l = priv->watch_directory_roots;
+
+ if (!roots) {
+ priv->watch_directory_roots = NULL;
+ } else {
+ priv->watch_directory_roots = tracker_gslist_copy_with_string_data (roots);
+ }
+
+ g_slist_foreach (l, (GFunc) g_free, NULL);
+ g_slist_free (l);
+
+ g_object_notify (G_OBJECT (config), "watch-directory-roots");
+}
+
+void
+tracker_config_set_crawl_directory_roots (TrackerConfig *config,
+ GSList *roots)
+{
+ TrackerConfigPrivate *priv;
+ GSList *l;
+
+ g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+ l = priv->crawl_directory_roots;
+
+ if (!roots) {
+ priv->crawl_directory_roots = NULL;
+ } else {
+ priv->crawl_directory_roots = tracker_gslist_copy_with_string_data (roots);
+ }
+
+ g_slist_foreach (l, (GFunc) g_free, NULL);
+ g_slist_free (l);
+
+ g_object_notify (G_OBJECT (config), "crawl-directory-roots");
+}
+
+void
+tracker_config_set_no_watch_directory_roots (TrackerConfig *config,
+ GSList *roots)
+{
+ TrackerConfigPrivate *priv;
+ GSList *l;
+
+ g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+ l = priv->no_watch_directory_roots;
+
+ if (!roots) {
+ priv->no_watch_directory_roots = NULL;
+ } else {
+ priv->no_watch_directory_roots = tracker_gslist_copy_with_string_data (roots);
+ }
+
+ g_slist_foreach (l, (GFunc) g_free, NULL);
+ g_slist_free (l);
+
+ g_object_notify (G_OBJECT (config), "no-watch-directory-roots");
+}
+
+void
+tracker_config_set_disabled_modules (TrackerConfig *config,
+ GSList *modules)
+{
+ TrackerConfigPrivate *priv;
+ GSList *l;
+
+ g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+ priv = TRACKER_CONFIG_GET_PRIVATE (config);
+
+ l = priv->disabled_modules;
+
+ if (!modules) {
+ priv->disabled_modules = NULL;
+ } else {
+ priv->disabled_modules = tracker_gslist_copy_with_string_data (modules);
+ }
+
+ g_slist_foreach (l, (GFunc) g_free, NULL);
+ g_slist_free (l);
+
+ g_object_notify (G_OBJECT (config), "disabled-modules");
+}
diff --git a/src/libtracker-miner/tracker-config.h b/src/libtracker-miner/tracker-config.h
new file mode 100644
index 0000000..446e2b0
--- /dev/null
+++ b/src/libtracker-miner/tracker-config.h
@@ -0,0 +1,119 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009%, Nokia (urho konttori nokia com)
+ *
+ * This library 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 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#ifndef __TRACKER_MINER_FS_CONFIG_H__
+#define __TRACKER_MINER_FS_CONFIG_H__
+
+#include <glib-object.h>
+
+#include <libtracker-common/tracker-config-file.h>
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_CONFIG (tracker_config_get_type ())
+#define TRACKER_CONFIG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), TRACKER_TYPE_CONFIG, TrackerConfig))
+#define TRACKER_CONFIG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), TRACKER_TYPE_CONFIG, TrackerConfigClass))
+#define TRACKER_IS_CONFIG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), TRACKER_TYPE_CONFIG))
+#define TRACKER_IS_CONFIG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), TRACKER_TYPE_CONFIG))
+#define TRACKER_CONFIG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TRACKER_TYPE_CONFIG, TrackerConfigClass))
+
+typedef struct TrackerConfig TrackerConfig;
+typedef struct TrackerConfigClass TrackerConfigClass;
+
+struct TrackerConfig {
+ TrackerConfigFile parent;
+};
+
+struct TrackerConfigClass {
+ TrackerConfigFileClass parent_class;
+};
+
+GType tracker_config_get_type (void) G_GNUC_CONST;
+
+TrackerConfig *tracker_config_new (void);
+gboolean tracker_config_save (TrackerConfig *config);
+
+gint tracker_config_get_verbosity (TrackerConfig *config);
+gint tracker_config_get_initial_sleep (TrackerConfig *config);
+GSList * tracker_config_get_watch_directory_roots (TrackerConfig *config);
+GSList * tracker_config_get_crawl_directory_roots (TrackerConfig *config);
+GSList * tracker_config_get_no_watch_directory_roots (TrackerConfig *config);
+gboolean tracker_config_get_enable_watches (TrackerConfig *config);
+gint tracker_config_get_throttle (TrackerConfig *config);
+gboolean tracker_config_get_enable_thumbnails (TrackerConfig *config);
+GSList * tracker_config_get_disabled_modules (TrackerConfig *config);
+gboolean tracker_config_get_disable_indexing_on_battery (TrackerConfig *config);
+gboolean tracker_config_get_disable_indexing_on_battery_init (TrackerConfig *config);
+gint tracker_config_get_low_disk_space_limit (TrackerConfig *config);
+gboolean tracker_config_get_index_removable_devices (TrackerConfig *config);
+gboolean tracker_config_get_index_mounted_directories (TrackerConfig *config);
+
+void tracker_config_set_verbosity (TrackerConfig *config,
+ gint value);
+void tracker_config_set_initial_sleep (TrackerConfig *config,
+ gint value);
+void tracker_config_set_enable_watches (TrackerConfig *config,
+ gboolean value);
+void tracker_config_set_throttle (TrackerConfig *config,
+ gint value);
+void tracker_config_set_enable_thumbnails (TrackerConfig *config,
+ gboolean value);
+void tracker_config_set_disable_indexing_on_battery (TrackerConfig *config,
+ gboolean value);
+void tracker_config_set_disable_indexing_on_battery_init (TrackerConfig *config,
+ gboolean value);
+void tracker_config_set_low_disk_space_limit (TrackerConfig *config,
+ gint value);
+void tracker_config_set_index_removable_devices (TrackerConfig *config,
+ gboolean value);
+void tracker_config_set_index_mounted_directories (TrackerConfig *config,
+ gboolean value);
+
+/* List APIs*/
+void tracker_config_add_watch_directory_roots (TrackerConfig *config,
+ gchar * const *roots);
+void tracker_config_add_crawl_directory_roots (TrackerConfig *config,
+ gchar * const *roots);
+void tracker_config_add_no_watch_directory_roots (TrackerConfig *config,
+ gchar * const *roots);
+void tracker_config_add_disabled_modules (TrackerConfig *config,
+ const gchar * const *modules);
+void tracker_config_remove_watch_directory_roots (TrackerConfig *config,
+ const gchar *root);
+void tracker_config_remove_crawl_directory_roots (TrackerConfig *config,
+ const gchar *root);
+void tracker_config_remove_no_watch_directory_roots (TrackerConfig *config,
+ const gchar *root);
+void tracker_config_remove_disabled_modules (TrackerConfig *config,
+ const gchar *module);
+
+void tracker_config_set_watch_directory_roots (TrackerConfig *config,
+ GSList *roots);
+void tracker_config_set_crawl_directory_roots (TrackerConfig *config,
+ GSList *roots);
+void tracker_config_set_no_watch_directory_roots (TrackerConfig *config,
+ GSList *roots);
+void tracker_config_set_disabled_modules (TrackerConfig *config,
+ GSList *modules);
+
+G_END_DECLS
+
+#endif /* __TRACKER_MINER_FS_CONFIG_H__ */
+
diff --git a/src/libtracker-miner/tracker-crawler.c b/src/libtracker-miner/tracker-crawler.c
new file mode 100644
index 0000000..1cc1be3
--- /dev/null
+++ b/src/libtracker-miner/tracker-crawler.c
@@ -0,0 +1,1236 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+ *
+ * This library 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 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.h"
+
+#include <string.h>
+
+#include <gio/gio.h>
+
+#include <libtracker-common/tracker-dbus.h>
+#include <libtracker-common/tracker-file-utils.h>
+#include <libtracker-common/tracker-type-utils.h>
+#include <libtracker-common/tracker-utils.h>
+#include <libtracker-common/tracker-module-config.h>
+
+#include "tracker-config.h"
+#include "tracker-crawler.h"
+#include "tracker-dbus.h"
+#include "tracker-monitor.h"
+#include "tracker-marshal.h"
+#include "tracker-status.h"
+#include "tracker-utils.h"
+
+#define TRACKER_CRAWLER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TRACKER_TYPE_CRAWLER, TrackerCrawlerPrivate))
+
+#define FILE_ATTRIBUTES \
+ G_FILE_ATTRIBUTE_STANDARD_NAME "," \
+ G_FILE_ATTRIBUTE_STANDARD_TYPE
+
+#define FILES_QUEUE_PROCESS_INTERVAL 2000
+#define FILES_QUEUE_PROCESS_MAX 5000
+
+/* This is the number of files to be called back with from GIO at a
+ * time so we don't get called back for every file.
+ */
+#define FILES_GROUP_SIZE 100
+
+struct _TrackerCrawlerPrivate {
+ TrackerConfig *config;
+
+ gchar *module_name;
+
+ /* Found data */
+ GQueue *directories;
+ GQueue *files;
+
+ /* Idle handler for processing found data */
+ guint idle_id;
+
+ /* Options */
+ gboolean use_module_paths;
+
+ /* Actual paths that exist which we are crawling:
+ *
+ * - 'Paths' are non-recursive.
+ * - 'Recurse Paths' are recursive.
+ * - 'Special Paths' are recursive but not in module config.
+ */
+ GSList *paths;
+ GSList *paths_current;
+ gboolean paths_are_done;
+
+ GSList *recurse_paths;
+ GSList *recurse_paths_current;
+ gboolean recurse_paths_are_done;
+
+ GSList *special_paths;
+ GSList *special_paths_current;
+ gboolean special_paths_are_done;
+
+ /* Ignore/Index patterns */
+ GList *ignored_directory_patterns;
+ GList *ignored_file_patterns;
+ GList *index_file_patterns;
+ GList *ignored_directories_with_content;
+
+ /* Legacy NoWatchDirectoryRoots */
+ GSList *no_watch_directory_roots;
+ GSList *watch_directory_roots;
+ GSList *crawl_directory_roots;
+
+ /* Statistics */
+ GTimer *timer;
+ guint enumerations;
+ guint directories_found;
+ guint directories_ignored;
+ guint files_found;
+ guint files_ignored;
+
+ /* Status */
+ gboolean is_running;
+ gboolean is_finished;
+ gboolean was_started;
+};
+
+enum {
+ PROCESSING_DIRECTORY,
+ PROCESSING_FILE,
+ FINISHED,
+ LAST_SIGNAL
+};
+
+typedef struct {
+ GFile *child;
+ gboolean is_dir;
+} EnumeratorChildData;
+
+typedef struct {
+ TrackerCrawler *crawler;
+ GFile *parent;
+ GHashTable *children;
+} EnumeratorData;
+
+static void tracker_crawler_finalize (GObject *object);
+static void file_enumerate_next (GFileEnumerator *enumerator,
+ EnumeratorData *ed);
+static void file_enumerate_children (TrackerCrawler *crawler,
+ GFile *file);
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE(TrackerCrawler, tracker_crawler, G_TYPE_OBJECT)
+
+static void
+tracker_crawler_class_init (TrackerCrawlerClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = tracker_crawler_finalize;
+
+ signals[PROCESSING_DIRECTORY] =
+ g_signal_new ("processing-directory",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ tracker_marshal_VOID__STRING_OBJECT,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_STRING,
+ G_TYPE_OBJECT);
+ signals[PROCESSING_FILE] =
+ g_signal_new ("processing-file",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ tracker_marshal_VOID__STRING_OBJECT,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_STRING,
+ G_TYPE_OBJECT);
+ signals[FINISHED] =
+ g_signal_new ("finished",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ tracker_marshal_VOID__STRING_UINT_UINT_UINT_UINT,
+ G_TYPE_NONE,
+ 5,
+ G_TYPE_STRING,
+ G_TYPE_UINT,
+ G_TYPE_UINT,
+ G_TYPE_UINT,
+ G_TYPE_UINT);
+
+ g_type_class_add_private (object_class, sizeof (TrackerCrawlerPrivate));
+}
+
+static void
+tracker_crawler_init (TrackerCrawler *object)
+{
+ TrackerCrawlerPrivate *priv;
+
+ object->private = TRACKER_CRAWLER_GET_PRIVATE (object);
+
+ priv = object->private;
+
+ priv->directories = g_queue_new ();
+ priv->files = g_queue_new ();
+}
+
+static void
+tracker_crawler_finalize (GObject *object)
+{
+ TrackerCrawlerPrivate *priv;
+
+ priv = TRACKER_CRAWLER_GET_PRIVATE (object);
+
+ if (priv->timer) {
+ g_timer_destroy (priv->timer);
+ }
+
+ g_slist_foreach (priv->no_watch_directory_roots, (GFunc) g_free, NULL);
+ g_slist_free (priv->no_watch_directory_roots);
+
+ g_slist_foreach (priv->watch_directory_roots, (GFunc) g_free, NULL);
+ g_slist_free (priv->watch_directory_roots);
+
+ g_slist_foreach (priv->crawl_directory_roots, (GFunc) g_free, NULL);
+ g_slist_free (priv->crawl_directory_roots);
+
+ if (priv->index_file_patterns) {
+ g_list_free (priv->index_file_patterns);
+ }
+
+ if (priv->ignored_directory_patterns) {
+ g_list_free (priv->ignored_directory_patterns);
+ }
+
+ if (priv->ignored_file_patterns) {
+ g_list_free (priv->ignored_file_patterns);
+ }
+
+ if (priv->ignored_directories_with_content) {
+ g_list_free (priv->ignored_directories_with_content);
+ }
+
+ /* Don't free the 'current_' variant of these, they are just
+ * place holders so we know our status.
+ */
+ g_slist_foreach (priv->paths, (GFunc) g_free, NULL);
+ g_slist_free (priv->paths);
+
+ g_slist_foreach (priv->recurse_paths, (GFunc) g_free, NULL);
+ g_slist_free (priv->recurse_paths);
+
+ g_slist_foreach (priv->special_paths, (GFunc) g_free, NULL);
+ g_slist_free (priv->special_paths);
+
+ if (priv->idle_id) {
+ g_source_remove (priv->idle_id);
+ }
+
+ g_queue_foreach (priv->files, (GFunc) g_object_unref, NULL);
+ g_queue_free (priv->files);
+
+ g_queue_foreach (priv->directories, (GFunc) g_object_unref, NULL);
+ g_queue_free (priv->directories);
+
+ g_free (priv->module_name);
+
+ g_object_unref (priv->config);
+
+ G_OBJECT_CLASS (tracker_crawler_parent_class)->finalize (object);
+}
+
+TrackerCrawler *
+tracker_crawler_new (TrackerConfig *config,
+ const gchar *module_name)
+{
+ TrackerCrawler *crawler;
+
+ g_return_val_if_fail (TRACKER_IS_CONFIG (config), NULL);
+ g_return_val_if_fail (module_name != NULL, NULL);
+
+ crawler = g_object_new (TRACKER_TYPE_CRAWLER, NULL);
+
+ crawler->private->config = g_object_ref (config);
+
+ crawler->private->module_name = g_strdup (module_name);
+
+ /* Set up crawl data */
+ crawler->private->ignored_directory_patterns =
+ tracker_module_config_get_ignored_directory_patterns (module_name);
+ crawler->private->ignored_file_patterns =
+ tracker_module_config_get_ignored_file_patterns (module_name);
+ crawler->private->index_file_patterns =
+ tracker_module_config_get_index_file_patterns (module_name);
+ crawler->private->ignored_directories_with_content =
+ tracker_module_config_get_ignored_directories_with_content (module_name);
+
+ /* Should we use module config paths? If true, when we
+ * _start() the module config paths are used to import paths
+ * to crawl. By default this is TRUE.
+ */
+ crawler->private->use_module_paths = TRUE;
+
+ return crawler;
+}
+
+/*
+ * Functions
+ */
+
+static gboolean
+is_path_ignored (TrackerCrawler *crawler,
+ const gchar *path,
+ gboolean is_directory)
+{
+ GList *l;
+ gchar *basename;
+ gboolean ignore;
+
+ if (tracker_is_empty_string (path)) {
+ return TRUE;
+ }
+
+ if (!g_utf8_validate (path, -1, NULL)) {
+ g_message ("Ignoring path:'%s', not valid UTF-8", path);
+ return TRUE;
+ }
+
+ if (is_directory) {
+ GSList *sl;
+
+ /* Most common things to ignore */
+ if (strcmp (path, "/dev") == 0 ||
+ strcmp (path, "/lib") == 0 ||
+ strcmp (path, "/proc") == 0 ||
+ strcmp (path, "/sys") == 0) {
+ return TRUE;
+ }
+
+ if (g_str_has_prefix (path, g_get_tmp_dir ())) {
+ return TRUE;
+ }
+
+ /* Check ignored directories in config */
+ for (sl = crawler->private->no_watch_directory_roots; sl; sl = sl->next) {
+ if (strcmp (sl->data, path) == 0) {
+ return TRUE;
+ }
+ }
+ }
+
+ /* Check basename against pattern matches */
+ basename = g_path_get_basename (path);
+ ignore = TRUE;
+
+ if (!basename) {
+ goto done;
+ }
+
+ /* Test ignore types */
+ if (is_directory) {
+ GSList *sl;
+
+ /* If directory begins with ".", check it isn't one of
+ * the top level directories to watch/crawl if it
+ * isn't we ignore it. If it is, we don't.
+ */
+ if (basename[0] == '.') {
+ for (sl = crawler->private->watch_directory_roots; sl; sl = sl->next) {
+ if (strcmp (sl->data, path) == 0) {
+ ignore = FALSE;
+ goto done;
+ }
+ }
+
+ for (sl = crawler->private->crawl_directory_roots; sl; sl = sl->next) {
+ if (strcmp (sl->data, path) == 0) {
+ ignore = FALSE;
+ goto done;
+ }
+ }
+
+ goto done;
+ }
+
+ /* Check module directory ignore patterns */
+ for (l = crawler->private->ignored_directory_patterns; l; l = l->next) {
+ if (g_pattern_match_string (l->data, basename)) {
+ goto done;
+ }
+ }
+ } else {
+ if (basename[0] == '.') {
+ goto done;
+ }
+
+ /* Check module file ignore patterns */
+ for (l = crawler->private->ignored_file_patterns; l; l = l->next) {
+ if (g_pattern_match_string (l->data, basename)) {
+ goto done;
+ }
+ }
+
+ /* Check module file match patterns */
+ for (l = crawler->private->index_file_patterns; l; l = l->next) {
+ if (!g_pattern_match_string (l->data, basename)) {
+ goto done;
+ }
+ }
+ }
+
+ ignore = FALSE;
+
+done:
+ g_free (basename);
+
+ return ignore;
+}
+
+static void
+add_file (TrackerCrawler *crawler,
+ GFile *file)
+{
+ gchar *path;
+
+ g_return_if_fail (G_IS_FILE (file));
+
+ path = g_file_get_path (file);
+
+ if (is_path_ignored (crawler, path, FALSE)) {
+ crawler->private->files_ignored++;
+
+ g_debug ("Ignored:'%s' (%d)",
+ path,
+ crawler->private->enumerations);
+ } else {
+ crawler->private->files_found++;
+
+ g_debug ("Found :'%s' (%d)",
+ path,
+ crawler->private->enumerations);
+
+ g_queue_push_tail (crawler->private->files, g_object_ref (file));
+ }
+
+ g_free (path);
+}
+
+static void
+add_directory (TrackerCrawler *crawler,
+ GFile *file)
+{
+ gchar *path;
+
+ g_return_if_fail (G_IS_FILE (file));
+
+ path = g_file_get_path (file);
+
+ if (is_path_ignored (crawler, path, TRUE)) {
+ crawler->private->directories_ignored++;
+
+ g_debug ("Ignored:'%s' (%d)",
+ path,
+ crawler->private->enumerations);
+ } else {
+ g_debug ("Found :'%s' (%d)",
+ path,
+ crawler->private->enumerations);
+
+ g_queue_push_tail (crawler->private->directories, g_object_ref (file));
+ }
+
+ g_free (path);
+}
+
+static void
+process_file (TrackerCrawler *crawler,
+ GFile *file)
+{
+ g_signal_emit (crawler, signals[PROCESSING_FILE], 0,
+ crawler->private->module_name, file);
+}
+
+static void
+process_directory (TrackerCrawler *crawler,
+ GFile *file)
+{
+ file_enumerate_children (crawler, file);
+}
+
+static gboolean
+process_func (gpointer data)
+{
+ TrackerCrawler *crawler;
+ TrackerCrawlerPrivate *priv;
+ GFile *file;
+
+ crawler = TRACKER_CRAWLER (data);
+ priv = crawler->private;
+
+ /* If manually paused, we hold off until unpaused */
+ if (tracker_status_get_is_paused_manually () ||
+ tracker_status_get_is_paused_for_io ()) {
+ return TRUE;
+ }
+
+ /* Throttle the crawler, with testing, throttling every item
+ * took the time to crawl 130k files from 7 seconds up to 68
+ * seconds. So it is important to get this figure right.
+ */
+ tracker_throttle (priv->config, 25);
+
+ /* Crawler files */
+ file = g_queue_pop_head (priv->files);
+
+ if (file) {
+ process_file (crawler, file);
+ g_object_unref (file);
+
+ return TRUE;
+ }
+
+ /* Crawler directories */
+ file = g_queue_pop_head (priv->directories);
+
+ if (file) {
+ process_directory (crawler, file);
+ g_object_unref (file);
+
+ return TRUE;
+ }
+
+ /* If we still have some async operations in progress, wait
+ * for them to finish, if not, we are truly done.
+ */
+ if (priv->enumerations > 0) {
+ return TRUE;
+ }
+
+ /* Process next path in list */
+ if (!priv->paths_are_done) {
+ /* This is done so we don't go over the list again
+ * when we get to the end and the current item = NULL.
+ */
+ priv->paths_are_done = TRUE;
+
+ if (!priv->paths_current) {
+ priv->paths_current = priv->paths;
+ }
+ } else {
+ if (priv->paths_current) {
+ priv->paths_current = priv->paths_current->next;
+ }
+ }
+
+ if (priv->paths_current) {
+ g_message (" Searching directory:'%s'",
+ (gchar*) priv->paths_current->data);
+
+ file = g_file_new_for_path (priv->paths_current->data);
+ add_directory (crawler, file);
+ g_object_unref (file);
+
+ return TRUE;
+ }
+
+ /* Process next recursive path in list */
+ if (!priv->recurse_paths_are_done) {
+ /* This is done so we don't go over the list again
+ * when we get to the end and the current item = NULL.
+ */
+ priv->recurse_paths_are_done = TRUE;
+
+ if (!priv->recurse_paths_current) {
+ priv->recurse_paths_current = priv->recurse_paths;
+ }
+ } else {
+ if (priv->recurse_paths_current) {
+ priv->recurse_paths_current = priv->recurse_paths_current->next;
+ }
+ }
+
+ if (priv->recurse_paths_current) {
+ g_message (" Searching directory:'%s' (recursively)",
+ (gchar *) priv->recurse_paths_current->data);
+
+ file = g_file_new_for_path (priv->recurse_paths_current->data);
+ add_directory (crawler, file);
+ g_object_unref (file);
+
+ return TRUE;
+ }
+
+ /* Process next special path in list */
+ if (!priv->special_paths_are_done) {
+ /* This is done so we don't go over the list again
+ * when we get to the end and the current item = NULL.
+ */
+ priv->special_paths_are_done = TRUE;
+
+ if (!priv->special_paths_current) {
+ priv->special_paths_current = priv->special_paths;
+ }
+ } else {
+ if (priv->special_paths_current) {
+ priv->special_paths_current = priv->special_paths_current->next;
+ }
+ }
+
+ if (priv->special_paths_current) {
+ g_message (" Searching directory:'%s' (special)",
+ (gchar *) priv->special_paths_current->data);
+
+ file = g_file_new_for_path (priv->special_paths_current->data);
+ add_directory (crawler, file);
+ g_object_unref (file);
+
+ return TRUE;
+ }
+
+ priv->idle_id = 0;
+ priv->is_finished = TRUE;
+
+ tracker_crawler_stop (crawler);
+
+ return FALSE;
+}
+
+static EnumeratorChildData *
+enumerator_child_data_new (GFile *child,
+ gboolean is_dir)
+{
+ EnumeratorChildData *cd;
+
+ cd = g_slice_new (EnumeratorChildData);
+
+ cd->child = g_object_ref (child);
+ cd->is_dir = is_dir;
+
+ return cd;
+}
+
+static void
+enumerator_child_data_free (EnumeratorChildData *cd)
+{
+ g_object_unref (cd->child);
+ g_slice_free (EnumeratorChildData, cd);
+}
+
+static EnumeratorData *
+enumerator_data_new (TrackerCrawler *crawler,
+ GFile *parent)
+{
+ EnumeratorData *ed;
+
+ ed = g_slice_new0 (EnumeratorData);
+
+ ed->crawler = g_object_ref (crawler);
+ ed->parent = g_object_ref (parent);
+ ed->children = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) enumerator_child_data_free);
+ return ed;
+}
+
+static void
+enumerator_data_add_child (EnumeratorData *ed,
+ const gchar *name,
+ GFile *file,
+ gboolean is_dir)
+{
+ g_hash_table_insert (ed->children,
+ g_strdup (name),
+ enumerator_child_data_new (file, is_dir));
+}
+
+static void
+enumerator_data_process (EnumeratorData *ed)
+{
+ TrackerCrawler *crawler;
+ GHashTableIter iter;
+ EnumeratorChildData *cd;
+ GList *l;
+
+ crawler = ed->crawler;
+
+ /* Ignore directory if its contents match something we should ignore */
+ for (l = crawler->private->ignored_directories_with_content; l; l = l->next) {
+ if (g_hash_table_lookup (ed->children, l->data)) {
+ gchar *path;
+
+ path = g_file_get_path (ed->parent);
+
+ crawler->private->directories_ignored++;
+ g_debug ("Ignoring directory '%s' since it contains a file named '%s'", path, (gchar *) l->data);
+ g_free (path);
+
+ return;
+ }
+ }
+
+ crawler->private->directories_found++;
+ g_signal_emit (crawler, signals[PROCESSING_DIRECTORY], 0,
+ crawler->private->module_name, ed->parent);
+
+ g_hash_table_iter_init (&iter, ed->children);
+
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &cd)) {
+ if (cd->is_dir) {
+ /* This is a bit of a hack, but we assume this is a
+ * recursive lookup because the current non-recursive
+ * path is NULL, meaning they have all been traversed
+ * already.
+ */
+ if (crawler->private->paths_are_done) {
+ add_directory (crawler, cd->child);
+ }
+ } else {
+ add_file (crawler, cd->child);
+ }
+ }
+}
+
+static void
+enumerator_data_free (EnumeratorData *ed)
+{
+ g_object_unref (ed->parent);
+ g_object_unref (ed->crawler);
+ g_hash_table_destroy (ed->children);
+ g_slice_free (EnumeratorData, ed);
+}
+
+static void
+file_enumerator_close_cb (GObject *enumerator,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ TrackerCrawler *crawler;
+ GError *error = NULL;
+
+ crawler = TRACKER_CRAWLER (user_data);
+ crawler->private->enumerations--;
+
+ if (!g_file_enumerator_close_finish (G_FILE_ENUMERATOR (enumerator),
+ result,
+ &error)) {
+ g_warning ("Couldn't close GFileEnumerator (%p): %s", enumerator,
+ (error) ? error->message : "No reason");
+
+ g_clear_error (&error);
+ }
+}
+
+static void
+file_enumerate_next_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ TrackerCrawler *crawler;
+ EnumeratorData *ed;
+ GFileEnumerator *enumerator;
+ GFile *parent, *child;
+ GFileInfo *info;
+ GList *files, *l;
+ GError *error = NULL;
+
+ enumerator = G_FILE_ENUMERATOR (object);
+
+ ed = (EnumeratorData*) user_data;
+ crawler = ed->crawler;
+ parent = ed->parent;
+
+ files = g_file_enumerator_next_files_finish (enumerator,
+ result,
+ &error);
+
+ if (error || !files || !crawler->private->is_running) {
+ if (error) {
+ g_critical ("Could not crawl through directory: %s", error->message);
+ g_error_free (error);
+ }
+
+ /* No more files or we are stopping anyway, so clean
+ * up and close all file enumerators.
+ */
+ if (files) {
+ g_list_foreach (files, (GFunc) g_object_unref, NULL);
+ g_list_free (files);
+ }
+
+ enumerator_data_process (ed);
+ enumerator_data_free (ed);
+ g_file_enumerator_close_async (enumerator,
+ G_PRIORITY_DEFAULT,
+ NULL,
+ file_enumerator_close_cb,
+ crawler);
+ g_object_unref (enumerator);
+
+ return;
+ }
+
+ for (l = files; l; l = l->next) {
+ const gchar *child_name;
+ gboolean is_dir;
+
+ info = l->data;
+
+ child_name = g_file_info_get_name (info);
+ child = g_file_get_child (parent, child_name);
+ is_dir = (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY);
+
+ enumerator_data_add_child (ed, child_name, child, is_dir);
+
+ g_object_unref (child);
+ g_object_unref (info);
+ }
+
+ g_list_free (files);
+
+ /* Get next files */
+ file_enumerate_next (enumerator, ed);
+}
+
+static void
+file_enumerate_next (GFileEnumerator *enumerator,
+ EnumeratorData *ed)
+{
+ g_file_enumerator_next_files_async (enumerator,
+ FILES_GROUP_SIZE,
+ G_PRIORITY_LOW,
+ NULL,
+ file_enumerate_next_cb,
+ ed);
+}
+
+static void
+file_enumerate_children_cb (GObject *file,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ TrackerCrawler *crawler;
+ EnumeratorData *ed;
+ GFileEnumerator *enumerator;
+ GFile *parent;
+ GError *error = NULL;
+
+ parent = G_FILE (file);
+ ed = (EnumeratorData *) user_data;
+ crawler = ed->crawler;
+ enumerator = g_file_enumerate_children_finish (parent, result, &error);
+
+ if (!enumerator) {
+ if (error) {
+ gchar *uri;
+
+ uri = g_file_get_uri (parent);
+
+ g_critical ("Could not open directory '%s': %s",
+ uri, error->message);
+
+ g_error_free (error);
+ g_free (uri);
+ }
+
+ crawler->private->enumerations--;
+ return;
+ }
+
+ /* Start traversing the directory's files */
+ file_enumerate_next (enumerator, ed);
+}
+
+static void
+file_enumerate_children (TrackerCrawler *crawler,
+ GFile *file)
+{
+ EnumeratorData *ed;
+
+ crawler->private->enumerations++;
+
+ ed = enumerator_data_new (crawler, file);
+
+ g_file_enumerate_children_async (file,
+ FILE_ATTRIBUTES,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ G_PRIORITY_DEFAULT,
+ NULL,
+ file_enumerate_children_cb,
+ ed);
+}
+
+static GSList *
+prune_none_existing_gslist_paths (TrackerCrawler *crawler,
+ GSList *paths,
+ const gchar *path_type)
+{
+ TrackerCrawlerPrivate *priv;
+ GSList *new_paths = NULL;
+
+ priv = crawler->private;
+
+ if (paths) {
+ GSList *l;
+ GFile *file;
+ gchar *path;
+ gboolean exists;
+
+ /* Check the currently set recurse paths are real */
+ for (l = paths; l; l = l->next) {
+ path = l->data;
+
+ /* Check location exists before we do anything */
+ file = g_file_new_for_path (path);
+ exists = g_file_query_exists (file, NULL);
+
+ if (exists) {
+ g_message (" Directory:'%s' added to list to crawl exists %s%s%s",
+ path,
+ path_type ? "(" : "",
+ path_type ? path_type : "",
+ path_type ? ")" : "");
+ new_paths = g_slist_prepend (new_paths, g_strdup (path));
+ } else {
+ g_message (" Directory:'%s' does not exist",
+ path);
+ }
+
+ g_object_unref (file);
+ }
+
+ new_paths = g_slist_reverse (new_paths);
+ }
+
+ return new_paths;
+}
+
+static GSList *
+prune_none_existing_glist_paths (TrackerCrawler *crawler,
+ GList *paths,
+ const gchar *path_type)
+{
+ GSList *temporary_paths;
+ GSList *new_paths;
+ GList *l;
+
+ /* The only difference with this function is that it takes a
+ * GList instead of a GSList.
+ */
+
+ temporary_paths = NULL;
+
+ for (l = paths; l; l = l->next) {
+ temporary_paths = g_slist_prepend (temporary_paths, l->data);
+ }
+
+ temporary_paths = g_slist_reverse (temporary_paths);
+ new_paths = prune_none_existing_gslist_paths (crawler, temporary_paths, path_type);
+ g_slist_free (temporary_paths);
+
+ return new_paths;
+}
+
+gboolean
+tracker_crawler_start (TrackerCrawler *crawler)
+{
+ TrackerCrawlerPrivate *priv;
+ GSList *l;
+
+ g_return_val_if_fail (TRACKER_IS_CRAWLER (crawler), FALSE);
+
+ priv = crawler->private;
+
+ priv->was_started = TRUE;
+
+ g_message ("Crawling directories for module:'%s'",
+ crawler->private->module_name);
+
+ if (priv->use_module_paths) {
+ GSList *new_paths;
+ GList *recurse_paths;
+ GList *paths;
+
+ g_message (" Using module paths");
+
+ recurse_paths = tracker_module_config_get_monitor_recurse_directories (priv->module_name);
+ paths = tracker_module_config_get_monitor_directories (priv->module_name);
+
+ if (recurse_paths || paths) {
+ /* First we do non-recursive directories */
+ new_paths = prune_none_existing_glist_paths (crawler, paths, NULL);
+ g_slist_foreach (priv->paths, (GFunc) g_free, NULL);
+ g_slist_free (priv->paths);
+ priv->paths = new_paths;
+
+ /* Second we do recursive directories */
+ new_paths = prune_none_existing_glist_paths (crawler, recurse_paths, "recursively");
+ g_slist_foreach (priv->recurse_paths, (GFunc) g_free, NULL);
+ g_slist_free (priv->recurse_paths);
+ priv->recurse_paths = new_paths;
+ } else {
+ g_message (" No directories from module config");
+ }
+
+ g_list_free (paths);
+ g_list_free (recurse_paths);
+ } else {
+ GSList *new_paths;
+
+ g_message (" Not using module config paths, using special paths added");
+
+ new_paths = prune_none_existing_gslist_paths (crawler, priv->special_paths, "special");
+ g_slist_foreach (priv->special_paths, (GFunc) g_free, NULL);
+ g_slist_free (priv->special_paths);
+ priv->special_paths = new_paths;
+ }
+
+ if (!priv->paths && !priv->recurse_paths && !priv->special_paths) {
+ g_message (" No directories that actually exist to iterate, doing nothing");
+ return FALSE;
+ }
+
+ /* Filter duplicates */
+ l = priv->paths;
+ priv->paths = tracker_path_list_filter_duplicates (priv->paths, ".");
+ g_slist_foreach (l, (GFunc) g_free, NULL);
+ g_slist_free (l);
+
+ l = priv->recurse_paths;
+ priv->recurse_paths = tracker_path_list_filter_duplicates (priv->recurse_paths, ".");
+ g_slist_foreach (l, (GFunc) g_free, NULL);
+ g_slist_free (l);
+
+ l = priv->special_paths;
+ priv->special_paths = tracker_path_list_filter_duplicates (priv->special_paths, ".");
+ g_slist_foreach (l, (GFunc) g_free, NULL);
+ g_slist_free (l);
+
+ /* Set up legacy NoWatchDirectoryRoots so we don't have to get
+ * them from the config for EVERY file we traverse.
+ */
+ g_slist_foreach (priv->no_watch_directory_roots, (GFunc) g_free, NULL);
+ g_slist_free (priv->no_watch_directory_roots);
+ l = tracker_config_get_no_watch_directory_roots (priv->config);
+ priv->no_watch_directory_roots = tracker_gslist_copy_with_string_data (l);
+
+ g_slist_foreach (priv->watch_directory_roots, (GFunc) g_free, NULL);
+ g_slist_free (priv->watch_directory_roots);
+ l = tracker_config_get_watch_directory_roots (priv->config);
+ priv->watch_directory_roots = tracker_gslist_copy_with_string_data (l);
+
+ g_slist_foreach (priv->crawl_directory_roots, (GFunc) g_free, NULL);
+ g_slist_free (priv->crawl_directory_roots);
+ l = tracker_config_get_crawl_directory_roots (priv->config);
+ priv->crawl_directory_roots = tracker_gslist_copy_with_string_data (l);
+
+ /* Time the event */
+ if (priv->timer) {
+ g_timer_destroy (priv->timer);
+ }
+
+ priv->timer = g_timer_new ();
+
+ /* Set as running now */
+ priv->is_running = TRUE;
+ priv->is_finished = FALSE;
+
+ /* Reset stats */
+ priv->directories_found = 0;
+ priv->directories_ignored = 0;
+ priv->files_found = 0;
+ priv->files_ignored = 0;
+
+ /* Reset paths which have been iterated */
+ priv->paths_are_done = FALSE;
+ priv->recurse_paths_are_done = FALSE;
+ priv->special_paths_are_done = FALSE;
+
+ /* Set idle handler to process directories and files found */
+ priv->idle_id = g_idle_add (process_func, crawler);
+
+ return TRUE;
+}
+
+void
+tracker_crawler_stop (TrackerCrawler *crawler)
+{
+ TrackerCrawlerPrivate *priv;
+
+ g_return_if_fail (TRACKER_IS_CRAWLER (crawler));
+
+ priv = crawler->private;
+
+
+ g_message (" %s crawling files in %4.4f seconds",
+ priv->is_finished ? "Finished" : "Stopped",
+ g_timer_elapsed (priv->timer, NULL));
+ g_message (" Found %d directories, ignored %d directories",
+ priv->directories_found,
+ priv->directories_ignored);
+ g_message (" Found %d files, ignored %d files",
+ priv->files_found,
+ priv->files_ignored);
+
+ priv->is_running = FALSE;
+
+ if (priv->idle_id) {
+ g_source_remove (priv->idle_id);
+ priv->idle_id = 0;
+ }
+
+ if (priv->timer) {
+ g_timer_destroy (priv->timer);
+ priv->timer = NULL;
+ }
+
+ g_signal_emit (crawler, signals[FINISHED], 0,
+ priv->module_name,
+ priv->directories_found,
+ priv->directories_ignored,
+ priv->files_found,
+ priv->files_ignored);
+}
+
+/* This function is a convenience for the monitor module so we can
+ * just ask it to crawl another path which we didn't know about
+ * before.
+ */
+void
+tracker_crawler_add_unexpected_path (TrackerCrawler *crawler,
+ const gchar *path)
+{
+ TrackerCrawlerPrivate *priv;
+ GFile *file;
+
+ g_return_if_fail (TRACKER_IS_CRAWLER (crawler));
+ g_return_if_fail (path != NULL);
+
+ priv = crawler->private;
+
+ /* This check should be fine, the reason being, that if we
+ * call this, it is because we have received a monitor event
+ * in the first place. This means we must already have been
+ * started at some point.
+ */
+ g_return_if_fail (priv->was_started);
+
+ /* FIXME: Should we check paths_are_done to see if we
+ * need to actually call add_directory()?
+ */
+ file = g_file_new_for_path (path);
+ add_directory (crawler, file);
+ g_object_unref (file);
+
+ /* FIXME: Should we reset the stats? */
+ if (!priv->idle_id) {
+ /* Time the event */
+ if (priv->timer) {
+ g_timer_destroy (priv->timer);
+ }
+
+ priv->timer = g_timer_new ();
+
+ /* Set as running now */
+ priv->is_running = TRUE;
+ priv->is_finished = FALSE;
+
+ /* Set idle handler to process directories and files found */
+ priv->idle_id = g_idle_add (process_func, crawler);
+ }
+}
+
+
+/* This is a convenience function to add extra locations because
+ * sometimes we want to add locations like the MMC or others to the
+ * "Files" module, for example.
+ */
+void
+tracker_crawler_special_paths_add (TrackerCrawler *crawler,
+ const gchar *path)
+{
+ TrackerCrawlerPrivate *priv;
+
+ g_return_if_fail (TRACKER_IS_CRAWLER (crawler));
+ g_return_if_fail (path != NULL);
+
+ priv = crawler->private;
+
+ g_return_if_fail (!priv->is_running);
+
+ priv->special_paths = g_slist_append (priv->special_paths, g_strdup (path));
+}
+
+void
+tracker_crawler_special_paths_clear (TrackerCrawler *crawler)
+{
+ TrackerCrawlerPrivate *priv;
+
+ g_return_if_fail (TRACKER_IS_CRAWLER (crawler));
+
+ priv = crawler->private;
+
+ g_return_if_fail (!priv->is_running);
+
+ g_slist_foreach (priv->special_paths, (GFunc) g_free, NULL);
+ g_slist_free (priv->special_paths);
+ priv->special_paths = NULL;
+}
+
+void
+tracker_crawler_use_module_paths (TrackerCrawler *crawler,
+ gboolean use_module_paths)
+{
+ TrackerCrawlerPrivate *priv;
+
+ g_return_if_fail (TRACKER_IS_CRAWLER (crawler));
+
+ priv = crawler->private;
+
+ g_return_if_fail (priv->is_running == FALSE);
+
+ priv->use_module_paths = use_module_paths;
+}
+
+gboolean
+tracker_crawler_is_path_ignored (TrackerCrawler *crawler,
+ const gchar *path,
+ gboolean is_directory)
+{
+ g_return_val_if_fail (TRACKER_IS_CRAWLER (crawler), TRUE);
+
+ /* We have an internal function here we call. The reason for
+ * this is that it is expensive to type check the Crawler
+ * object for EVERY file we process. Internally, we don't do
+ * that. Externally we do. Externally this is used by the
+ * processor when we get new monitor events to know if we
+ * should be sending new files to the indexer.
+ */
+ return is_path_ignored (crawler, path, is_directory);
+}
diff --git a/src/libtracker-miner/tracker-crawler.h b/src/libtracker-miner/tracker-crawler.h
new file mode 100644
index 0000000..8d6a8d8
--- /dev/null
+++ b/src/libtracker-miner/tracker-crawler.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+ *
+ * This library 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 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#ifndef __TRACKERD_CRAWLER_H__
+#define __TRACKERD_CRAWLER_H__
+
+#include <glib-object.h>
+
+#include "tracker-config.h"
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_CRAWLER (tracker_crawler_get_type ())
+#define TRACKER_CRAWLER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), TRACKER_TYPE_CRAWLER, TrackerCrawler))
+#define TRACKER_CRAWLER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TRACKER_TYPE_CRAWLER, TrackerCrawlerClass))
+#define TRACKER_IS_CRAWLER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), TRACKER_TYPE_CRAWLER))
+#define TRACKER_IS_CRAWLER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TRACKER_TYPE_CRAWLER))
+#define TRACKER_CRAWLER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TRACKER_TYPE_CRAWLER, TrackerCrawlerClass))
+
+typedef struct _TrackerCrawler TrackerCrawler;
+typedef struct _TrackerCrawlerClass TrackerCrawlerClass;
+typedef struct _TrackerCrawlerPrivate TrackerCrawlerPrivate;
+
+struct _TrackerCrawler {
+ GObject parent;
+ TrackerCrawlerPrivate *private;
+};
+
+struct _TrackerCrawlerClass {
+ GObjectClass parent;
+};
+
+GType tracker_crawler_get_type (void);
+TrackerCrawler *tracker_crawler_new (TrackerConfig *config,
+ const gchar *module_name);
+gboolean tracker_crawler_start (TrackerCrawler *crawler);
+void tracker_crawler_stop (TrackerCrawler *crawler);
+gboolean tracker_crawler_is_path_ignored (TrackerCrawler *crawler,
+ const gchar *path,
+ gboolean is_directory);
+void tracker_crawler_add_unexpected_path (TrackerCrawler *crawler,
+ const gchar *path);
+
+/* Convenience API for old .cfg file */
+void tracker_crawler_special_paths_add (TrackerCrawler *crawler,
+ const gchar *path);
+void tracker_crawler_special_paths_clear (TrackerCrawler *crawler);
+void tracker_crawler_use_module_paths (TrackerCrawler *crawler,
+ gboolean use_module_paths);
+
+G_END_DECLS
+
+#endif /* __TRACKERD_CRAWLER_H__ */
diff --git a/src/libtracker-miner/tracker-marshal.list b/src/libtracker-miner/tracker-marshal.list
new file mode 100644
index 0000000..5bba08b
--- /dev/null
+++ b/src/libtracker-miner/tracker-marshal.list
@@ -0,0 +1,8 @@
+VOID:DOUBLE,UINT,UINT,BOOL
+VOID:DOUBLE,STRING,UINT,UINT,UINT
+VOID:STRING,OBJECT,BOOLEAN
+VOID:STRING,OBJECT,OBJECT,BOOLEAN,BOOLEAN
+VOID:STRING,BOOL
+VOID:STRING,OBJECT
+VOID:STRING,UINT,UINT,UINT,UINT
+VOID:POINTER,STRING,STRING,STRING,STRING
diff --git a/src/libtracker-miner/tracker-miner-crawler.c b/src/libtracker-miner/tracker-miner-crawler.c
index 1c3bed8..8679f83 100644
--- a/src/libtracker-miner/tracker-miner-crawler.c
+++ b/src/libtracker-miner/tracker-miner-crawler.c
@@ -20,12 +20,18 @@
*/
#include "tracker-miner-crawler.h"
+#include "tracker-config.h"
+#include "tracker-processor.h"
+#include <libtracker-common/tracker-storage.h>
-#define TRACKER_MINER_CRAWLER_GET_PRIVATE (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRACKER_TYPE_MINER_CRAWLER, TrackerMinerCrawlerPrivate))
+#define TRACKER_MINER_CRAWLER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRACKER_TYPE_MINER_CRAWLER, TrackerMinerCrawlerPrivate))
typedef struct TrackerMinerCrawlerPrivate TrackerMinerCrawlerPrivate;
struct TrackerMinerCrawlerPrivate {
+ TrackerConfig *config;
+ TrackerStorage *storage;
+ TrackerProcessor *processor;
};
static void tracker_miner_crawler_finalize (GObject *object);
@@ -57,6 +63,14 @@ tracker_miner_crawler_class_init (TrackerMinerCrawlerClass *klass)
static void
tracker_miner_crawler_init (TrackerMinerCrawler *miner)
{
+ TrackerMinerCrawlerPrivate *priv;
+
+ priv = miner->_priv = TRACKER_MINER_CRAWLER_GET_PRIVATE (miner);
+
+ priv->config = tracker_config_new ();
+ priv->storage = tracker_storage_new ();
+
+ priv->processor = tracker_processor_new (priv->config, priv->storage);
}
static void
@@ -68,4 +82,11 @@ tracker_miner_crawler_finalize (GObject *object)
static void
tracker_miner_crawler_started (TrackerMiner *miner)
{
+ TrackerMinerCrawler *miner_crawler;
+ TrackerMinerCrawlerPrivate *priv;
+
+ miner_crawler = TRACKER_MINER_CRAWLER (miner);
+ priv = miner_crawler->_priv;
+
+ tracker_processor_start (priv->processor);
}
diff --git a/src/libtracker-miner/tracker-monitor.c b/src/libtracker-miner/tracker-monitor.c
new file mode 100644
index 0000000..c56dd64
--- /dev/null
+++ b/src/libtracker-miner/tracker-monitor.c
@@ -0,0 +1,1632 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+ *
+ * This library 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 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <libtracker-common/tracker-dbus.h>
+#include <libtracker-common/tracker-file-utils.h>
+#include <libtracker-common/tracker-module-config.h>
+
+#include <sys/inotify.h>
+#include <libinotify/libinotify.h>
+
+#include "tracker-monitor.h"
+#include "tracker-dbus.h"
+#include "tracker-marshal.h"
+#include "tracker-status.h"
+
+#define TRACKER_MONITOR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TRACKER_TYPE_MONITOR, TrackerMonitorPrivate))
+
+/* The life time of an item in the cache */
+#define CACHE_LIFETIME_SECONDS 1
+
+/* When we receive IO monitor events, we pause sending information to
+ * the indexer for a few seconds before continuing. We have to receive
+ * NO events for at least a few seconds before unpausing.
+ */
+#define PAUSE_ON_IO_SECONDS 5
+
+/* If this is defined, we pause the indexer when we get events. If it
+ * is not, we don't do any pausing.
+ */
+#undef PAUSE_ON_IO
+
+struct _TrackerMonitorPrivate {
+ TrackerConfig *config;
+
+ GHashTable *modules;
+
+ gboolean enabled;
+
+ GType monitor_backend;
+
+ 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;
+
+#ifdef PAUSE_ON_IO
+ /* Timeout id for pausing when we get IO */
+ guint unpause_timeout_id;
+#endif /* PAUSE_ON_IO */
+
+ GHashTable *event_pairs;
+ guint event_pairs_timeout_id;
+
+ GHashTable *cached_events;
+ guint cached_events_timeout_id;
+};
+
+typedef struct {
+ GFile *file;
+ GTimeVal start_time;
+ GTimeVal last_time;
+ guint32 event_type;
+} EventData;
+
+enum {
+ ITEM_CREATED,
+ ITEM_UPDATED,
+ ITEM_DELETED,
+ ITEM_MOVED,
+ LAST_SIGNAL
+};
+
+enum {
+ PROP_0,
+ PROP_ENABLED
+};
+
+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 EventData * event_data_new (GFile *file,
+ guint32 event_type);
+static void event_data_free (gpointer data);
+static guint get_inotify_limit (void);
+
+static INotifyHandle *libinotify_monitor_directory (TrackerMonitor *monitor,
+ GFile *file);
+static void libinotify_monitor_cancel (gpointer data);
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE(TrackerMonitor, tracker_monitor, G_TYPE_OBJECT)
+
+static void
+tracker_monitor_class_init (TrackerMonitorClass *klass)
+{
+ GObjectClass *object_class;
+
+ 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;
+
+ signals[ITEM_CREATED] =
+ g_signal_new ("item-created",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ tracker_marshal_VOID__STRING_OBJECT_BOOLEAN,
+ G_TYPE_NONE,
+ 3,
+ G_TYPE_STRING,
+ G_TYPE_OBJECT,
+ G_TYPE_BOOLEAN);
+ signals[ITEM_UPDATED] =
+ g_signal_new ("item-updated",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ tracker_marshal_VOID__STRING_OBJECT_BOOLEAN,
+ G_TYPE_NONE,
+ 3,
+ G_TYPE_STRING,
+ G_TYPE_OBJECT,
+ G_TYPE_BOOLEAN);
+ signals[ITEM_DELETED] =
+ g_signal_new ("item-deleted",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ tracker_marshal_VOID__STRING_OBJECT_BOOLEAN,
+ G_TYPE_NONE,
+ 3,
+ G_TYPE_STRING,
+ G_TYPE_OBJECT,
+ G_TYPE_BOOLEAN);
+ signals[ITEM_MOVED] =
+ g_signal_new ("item-moved",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ tracker_marshal_VOID__STRING_OBJECT_OBJECT_BOOLEAN_BOOLEAN,
+ G_TYPE_NONE,
+ 5,
+ G_TYPE_STRING,
+ G_TYPE_OBJECT,
+ G_TYPE_OBJECT,
+ 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_type_class_add_private (object_class, sizeof (TrackerMonitorPrivate));
+}
+
+static void
+tracker_monitor_init (TrackerMonitor *object)
+{
+ TrackerMonitorPrivate *priv;
+ GFile *file;
+ GFileMonitor *monitor;
+ GList *all_modules, *l;
+ const gchar *name;
+
+ object->private = TRACKER_MONITOR_GET_PRIVATE (object);
+
+ priv = object->private;
+
+ /* By default we enable monitoring */
+ priv->enabled = TRUE;
+
+ /* For each module we create a hash table for monitors */
+ priv->modules =
+ g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify) g_hash_table_unref);
+
+ /* We have a hash table with cookies so we can pair up move
+ * events.
+ */
+ priv->event_pairs =
+ g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ NULL,
+ event_data_free);
+
+ /* We have a hash table for events so we don't flood the
+ * indexer with the same events too frequently, we also
+ * concatenate some events with this like CREATED+UPDATED.
+ */
+ priv->cached_events =
+ g_hash_table_new_full (g_file_hash,
+ (GEqualFunc) g_file_equal,
+ g_object_unref,
+ event_data_free);
+
+ all_modules = tracker_module_config_get_modules ();
+
+ for (l = all_modules; l; l = l->next) {
+ GHashTable *monitors;
+
+ /* Create monitors table for this module */
+ monitors =
+ g_hash_table_new_full (g_file_hash,
+ (GEqualFunc) g_file_equal,
+ (GDestroyNotify) g_object_unref,
+ (GDestroyNotify) libinotify_monitor_cancel);
+
+ g_hash_table_insert (priv->modules, g_strdup (l->data), monitors);
+ }
+
+ g_list_free (all_modules);
+
+ /* 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_MOUNTS,
+ NULL,
+ NULL);
+
+ priv->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 (priv->monitor_backend);
+ if (name) {
+ /* Set limits based on backend... */
+ if (strcmp (name, "GInotifyDirectoryMonitor") == 0) {
+ /* Using inotify */
+ 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, "GFamDirectoryMonitor") == 0) {
+ /* Using Fam */
+ 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, "GFenDirectoryMonitor") == 0) {
+ /* Using Fen, what is this? */
+ g_message ("Monitor backend is Fen");
+
+ /* Guessing limit... */
+ priv->monitor_limit = 8192;
+ }
+ else if (strcmp (name, "GWin32DirectoryMonitor") == 0) {
+ /* Using Windows */
+ g_message ("Monitor backend is Windows");
+
+ /* Guessing limit... */
+ priv->monitor_limit = 8192;
+ }
+ else {
+ /* Unknown */
+ g_warning ("Monitor backend:'%s' is unknown, we have no limits "
+ "in place because we don't know what we are dealing with!",
+ name);
+
+ /* Guessing limit... */
+ priv->monitor_limit = 100;
+ }
+ }
+
+ g_message ("Monitor limit is %d", priv->monitor_limit);
+
+ g_file_monitor_cancel (monitor);
+ g_object_unref (monitor);
+ g_object_unref (file);
+}
+
+static void
+tracker_monitor_finalize (GObject *object)
+{
+ TrackerMonitorPrivate *priv;
+
+ priv = TRACKER_MONITOR_GET_PRIVATE (object);
+
+#ifdef PAUSE_ON_IO
+ if (priv->unpause_timeout_id) {
+ g_source_remove (priv->unpause_timeout_id);
+ }
+#endif /* PAUSE_ON_IO */
+
+ if (priv->cached_events_timeout_id) {
+ g_source_remove (priv->cached_events_timeout_id);
+ }
+
+ if (priv->event_pairs_timeout_id) {
+ g_source_remove (priv->event_pairs_timeout_id);
+ }
+
+ g_hash_table_unref (priv->cached_events);
+ g_hash_table_unref (priv->event_pairs);
+
+ g_hash_table_unref (priv->modules);
+
+ g_object_unref (priv->config);
+
+ 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_PRIVATE (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 EventData *
+event_data_new (GFile *file,
+ guint32 event_type)
+{
+ EventData *event;
+ GTimeVal now;
+
+ event = g_new0 (EventData, 1);
+ g_get_current_time (&now);
+
+ event->file = g_object_ref (file);
+ event->start_time = now;
+ event->last_time = now;
+ event->event_type = event_type;
+
+ return event;
+}
+
+static void
+event_data_update (EventData *event)
+{
+ GTimeVal now;
+
+ g_get_current_time (&now);
+
+ event->last_time = now;
+}
+
+static void
+event_data_free (gpointer data)
+{
+ EventData *event;
+
+ event = data;
+
+ g_object_unref (event->file);
+}
+
+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 const gchar *
+get_queue_from_gfile (GHashTable *modules,
+ GFile *file)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&iter, modules);
+
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ GHashTable *hash_table;
+
+ hash_table = value;
+
+ if (g_hash_table_lookup (hash_table, file)) {
+ return key;
+ }
+ }
+
+ return NULL;
+}
+
+static const gchar *
+get_module_name_from_gfile (TrackerMonitor *monitor,
+ GFile *file,
+ gboolean *is_directory)
+{
+ const gchar *module_name;
+
+ if (is_directory) {
+ *is_directory = TRUE;
+ }
+
+ /* First try to get the module name from the file, this will
+ * only work if the event we received is for a directory.
+ */
+ module_name = get_queue_from_gfile (monitor->private->modules, file);
+ if (!module_name) {
+ GFile *parent;
+
+ /* Second we try to get the module name from the base
+ * name of the file.
+ */
+ parent = g_file_get_parent (file);
+
+ if (!parent) {
+ gchar *path;
+
+ path = g_file_get_path (file);
+
+ g_debug ("Could not get module name from GFile (path:'%s')",
+ path);
+
+ g_free (path);
+
+ return NULL;
+ }
+
+ module_name = get_queue_from_gfile (monitor->private->modules, parent);
+
+ if (!module_name) {
+ gchar *parent_path;
+ gchar *child_path;
+
+ parent_path = g_file_get_path (parent);
+ child_path = g_file_get_path (file);
+
+ if (is_directory) {
+ *is_directory = g_file_test (child_path, G_FILE_TEST_IS_DIR);
+ }
+
+ g_debug ("Could not get module name from GFile (path:'%s' or parent:'%s')",
+ child_path,
+ parent_path);
+
+ g_free (parent_path);
+ g_free (child_path);
+ } else {
+ if (is_directory) {
+ gchar *child_path;
+
+ child_path = g_file_get_path (file);
+ *is_directory = g_file_test (child_path, G_FILE_TEST_IS_DIR);
+ g_free (child_path);
+ }
+ }
+
+ g_object_unref (parent);
+ }
+
+ return module_name;
+}
+
+#ifdef PAUSE_ON_IO
+
+static gboolean
+unpause_cb (gpointer data)
+{
+ TrackerMonitor *monitor;
+
+ monitor = data;
+
+ g_message ("Resuming indexing now we have stopped "
+ "receiving monitor events for %d seconds",
+ PAUSE_ON_IO_SECONDS);
+
+ monitor->private->unpause_timeout_id = 0;
+ tracker_status_set_is_paused_for_io (FALSE);
+
+ return FALSE;
+}
+
+#endif /* PAUSE_ON_IO */
+
+
+static gchar *
+libinotify_monitor_event_to_string (guint32 event_type)
+{
+ GString *s;
+
+ s = g_string_new ("");
+
+ if (event_type & IN_ACCESS) {
+ s = g_string_append (s, "IN_ACCESS | ");
+ }
+ if (event_type & IN_MODIFY) {
+ s = g_string_append (s, "IN_MODIFY | ");
+ }
+ if (event_type & IN_ATTRIB) {
+ s = g_string_append (s, "IN_ATTRIB | ");
+ }
+ if (event_type & IN_CLOSE_WRITE) {
+ s = g_string_append (s, "IN_CLOSE_WRITE | ");
+ }
+ if (event_type & IN_CLOSE_NOWRITE) {
+ s = g_string_append (s, "IN_CLOSE_NOWRITE | ");
+ }
+ if (event_type & IN_OPEN) {
+ s = g_string_append (s, "IN_OPEN | ");
+ }
+ if (event_type & IN_MOVED_FROM) {
+ s = g_string_append (s, "IN_MOVED_FROM | ");
+ }
+ if (event_type & IN_MOVED_TO) {
+ s = g_string_append (s, "IN_MOVED_TO | ");
+ }
+ if (event_type & IN_CREATE) {
+ s = g_string_append (s, "IN_CREATE | ");
+ }
+ if (event_type & IN_DELETE) {
+ s = g_string_append (s, "IN_DELETE | ");
+ }
+ if (event_type & IN_DELETE_SELF) {
+ s = g_string_append (s, "IN_DELETE_SELF | ");
+ }
+ if (event_type & IN_MOVE_SELF) {
+ s = g_string_append (s, "IN_MOVE_SELF | ");
+ }
+ if (event_type & IN_UNMOUNT) {
+ s = g_string_append (s, "IN_UNMOUNT | ");
+ }
+ if (event_type & IN_Q_OVERFLOW) {
+ s = g_string_append (s, "IN_Q_OVERFLOW | ");
+ }
+ if (event_type & IN_IGNORED) {
+ s = g_string_append (s, "IN_IGNORED | ");
+ }
+
+ /* helper events */
+ if (event_type & IN_CLOSE) {
+ s = g_string_append (s, "IN_CLOSE* | ");
+ }
+ if (event_type & IN_MOVE) {
+ s = g_string_append (s, "IN_MOVE* | ");
+ }
+
+ /* special flags */
+ if (event_type & IN_MASK_ADD) {
+ s = g_string_append (s, "IN_MASK_ADD^ | ");
+ }
+ if (event_type & IN_ISDIR) {
+ s = g_string_append (s, "IN_ISDIR^ | ");
+ }
+ if (event_type & IN_ONESHOT) {
+ s = g_string_append (s, "IN_ONESHOT^ | ");
+ }
+
+ s->str[s->len - 3] = '\0';
+
+ return g_string_free (s, FALSE);
+}
+
+static gboolean
+libinotify_event_pairs_timeout_cb (gpointer data)
+{
+ TrackerMonitor *monitor;
+ GTimeVal now;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ monitor = data;
+
+ g_debug ("Checking for event pairs which have timed out...");
+
+ g_get_current_time (&now);
+
+ g_hash_table_iter_init (&iter, monitor->private->event_pairs);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ EventData *event;
+ glong seconds;
+ glong seconds_then;
+ const gchar *module_name;
+ gboolean is_directory;
+
+ event = value;
+
+ seconds_then = event->start_time.tv_sec;
+
+ seconds = now.tv_sec;
+ seconds -= seconds_then;
+
+ g_debug ("Comparing now:%ld to then:%ld, diff:%ld",
+ now.tv_sec,
+ seconds_then,
+ seconds);
+
+ if (seconds < 2) {
+ continue;
+ }
+
+ /* We didn't receive an event pair for this
+ * cookie, so we just generate the CREATE or
+ * DELETE event for the file we know about.
+ */
+ g_debug ("Event:%d with cookie:%d has timed out (%ld seconds have elapsed)",
+ event->event_type,
+ GPOINTER_TO_UINT (key),
+ seconds);
+
+ module_name = get_module_name_from_gfile (monitor,
+ event->file,
+ &is_directory);
+
+ switch (event->event_type) {
+ case IN_MOVED_FROM:
+ case IN_DELETE:
+ case IN_DELETE_SELF:
+ /* So we knew the source, but not the
+ * target location for the event.
+ */
+ if (is_directory) {
+ tracker_monitor_remove (monitor,
+ module_name,
+ event->file);
+ }
+
+ g_signal_emit (monitor,
+ signals[ITEM_DELETED], 0,
+ module_name,
+ event->file,
+ is_directory);
+ break;
+
+ case IN_CREATE:
+ case IN_MOVED_TO:
+ /* So we knew the target, but not the
+ * source location for the event.
+ */
+ g_signal_emit (monitor,
+ signals[ITEM_CREATED], 0,
+ module_name,
+ event->file,
+ is_directory);
+ break;
+ default:
+ break;
+ }
+ /* Clean up */
+ g_hash_table_iter_remove (&iter);
+ }
+
+ if (g_hash_table_size (monitor->private->event_pairs) < 1) {
+ g_debug ("No more events to pair, removing timeout");
+ monitor->private->event_pairs_timeout_id = 0;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+libinotify_cached_event_handle (TrackerMonitor *monitor,
+ EventData *data,
+ const gchar *module_name,
+ gboolean is_directory)
+{
+ switch (data->event_type) {
+ case IN_MODIFY:
+ case IN_CLOSE_WRITE:
+ case IN_ATTRIB:
+ g_signal_emit (monitor,
+ signals[ITEM_UPDATED], 0,
+ module_name,
+ data->file,
+ is_directory);
+ break;
+
+ case IN_MOVED_FROM:
+ /* So we knew the source, but not the
+ * target location for the event.
+ */
+
+ case IN_DELETE:
+ case IN_DELETE_SELF:
+ if (is_directory) {
+ tracker_monitor_remove (monitor,
+ module_name,
+ data->file);
+ }
+
+ g_signal_emit (monitor,
+ signals[ITEM_DELETED], 0,
+ module_name,
+ data->file,
+ is_directory);
+
+ break;
+
+ case IN_MOVED_TO:
+ /* So we new the target, but not the
+ * source location for the event.
+ */
+
+ case IN_CREATE:
+ g_signal_emit (monitor,
+ signals[ITEM_CREATED], 0,
+ module_name,
+ data->file,
+ is_directory);
+
+ break;
+ default:
+ break;
+ }
+}
+
+static gboolean
+libinotify_cached_events_timeout_cb (gpointer data)
+{
+ TrackerMonitor *monitor;
+ GTimeVal now;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ monitor = data;
+
+ g_debug ("Checking for cached events that have timed out...");
+
+ g_get_current_time (&now);
+
+ g_hash_table_iter_init (&iter, monitor->private->cached_events);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ EventData *event;
+ glong last_event_seconds;
+ glong start_event_seconds;
+ gint cache_timeout;
+ gint scan_timeout;
+ const gchar *module_name;
+ gboolean is_directory;
+ gboolean force_emit = FALSE;
+ gboolean timed_out = FALSE;
+
+ event = value;
+
+ last_event_seconds = now.tv_sec - event->last_time.tv_sec;
+ start_event_seconds = now.tv_sec - event->start_time.tv_sec;
+
+ g_debug ("Comparing now:%ld to then:%ld (start:%ld), diff:%ld (with start:%ld)",
+ now.tv_sec,
+ event->last_time.tv_sec,
+ event->start_time.tv_sec,
+ last_event_seconds,
+ start_event_seconds);
+
+ module_name = get_module_name_from_gfile (monitor,
+ event->file,
+ &is_directory);
+
+ if (!module_name) {
+ /* File was deleted before we could check its
+ * cached events, just discard it
+ */
+ g_hash_table_iter_remove (&iter);
+ continue;
+ }
+
+ /* If the item has been in the cache for too long
+ * according to the module config options, then we
+ * force the cache to expire in order to not starve
+ * the indexer of events for files which are ALWAYS
+ * changing.
+ */
+ cache_timeout = tracker_module_config_get_cache_timeout (module_name);
+ scan_timeout = tracker_module_config_get_scan_timeout (module_name);
+
+ if (cache_timeout > 0) {
+ force_emit = start_event_seconds > cache_timeout;
+ }
+
+ timed_out = last_event_seconds >= MAX (CACHE_LIFETIME_SECONDS, scan_timeout);
+
+ /* Make sure the item is in the cache for at least 2
+ * seconds OR the time as stated by the module config
+ * options. This way, always changing files can not
+ * be emitted too and flood the indexer with events.
+ */
+ if (!force_emit) {
+ if (!timed_out) {
+ continue;
+ }
+
+ /* We didn't receive an event pair for this
+ * cookie, so we just generate the CREATE or
+ * DELETE event for the file we know about.
+ */
+ g_debug ("Cached event:%d has timed out (%ld seconds have elapsed)",
+ event->event_type,
+ last_event_seconds);
+ } else {
+ event->start_time = now;
+ g_debug ("Cached event:%d has been forced to signal (%ld seconds have elapsed since the beginning)",
+ event->event_type,
+ start_event_seconds);
+ }
+
+ /* Signal event */
+ libinotify_cached_event_handle (monitor,
+ event,
+ module_name,
+ is_directory);
+
+
+ if (timed_out) {
+ /* Clean up */
+ g_hash_table_iter_remove (&iter);
+ } else {
+ if (event->event_type == IN_CREATE) {
+ /* The file has been already created,
+ * We want any further events to be
+ * IN_MODIFY.
+ */
+ event->event_type = IN_MODIFY;
+ }
+ }
+ }
+
+ if (g_hash_table_size (monitor->private->cached_events) < 1) {
+ g_debug ("No more cached events, removing timeout");
+ monitor->private->cached_events_timeout_id = 0;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+libinotify_cached_event_delete_children_func (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ EventData *data;
+
+ data = user_data;
+
+ return (data->event_type == IN_DELETE ||
+ data->event_type == IN_DELETE_SELF) &&
+ g_file_has_prefix (key, data->file);
+}
+
+static void
+libinotify_monitor_force_emission (TrackerMonitor *monitor,
+ GFile *file,
+ guint32 event_type,
+ const gchar *module_name,
+ gboolean is_directory)
+{
+ EventData *data;
+
+ data = g_hash_table_lookup (monitor->private->cached_events, file);
+
+ if (data) {
+ gchar *event_type_str;
+
+ event_type_str = libinotify_monitor_event_to_string (event_type);
+
+ g_debug ("Cached event:%d being handled before %s",
+ data->event_type,
+ event_type_str);
+
+ /* Signal event */
+ libinotify_cached_event_handle (monitor,
+ data,
+ module_name,
+ is_directory);
+
+ /* Clean up */
+ g_hash_table_remove (monitor->private->cached_events, data->file);
+ }
+}
+
+static void
+libinotify_monitor_event_cb (INotifyHandle *handle,
+ const char *monitor_name,
+ const char *filename,
+ guint32 event_type,
+ guint32 cookie,
+ gpointer user_data)
+{
+ TrackerMonitor *monitor;
+ GFile *file;
+ GFile *other_file;
+ const gchar *module_name;
+ gchar *str1;
+ gchar *str2;
+ gboolean is_directory;
+ gchar *event_type_str;
+ EventData *data = NULL;
+ gboolean set_up_cache_timeout = FALSE;
+
+ monitor = user_data;
+
+ switch (event_type) {
+ case IN_Q_OVERFLOW:
+ case IN_OPEN:
+ case IN_CLOSE_NOWRITE:
+ case IN_ACCESS:
+ case IN_IGNORED:
+ /* Return, otherwise we spam with messages for every
+ * file we open and look at.
+ */
+ return;
+ default:
+ break;
+ }
+
+ if (G_UNLIKELY (!monitor->private->enabled)) {
+ g_debug ("Silently dropping monitor event, monitor disabled for now");
+ return;
+ }
+
+ if (monitor_name) {
+ str1 = g_build_filename (monitor_name, filename, NULL);
+ file = g_file_new_for_path (str1);
+ } else {
+ str1 = NULL;
+ file = g_file_new_for_path (filename);
+ }
+
+ other_file = NULL;
+ module_name = get_module_name_from_gfile (monitor, file, &is_directory);
+
+ if (!module_name) {
+ g_free (str1);
+ g_object_unref (file);
+ return;
+ }
+
+ if (!str1) {
+ str1 = g_file_get_path (file);
+ }
+
+ if (other_file) {
+ str2 = g_file_get_path (other_file);
+ } else {
+ str2 = NULL;
+ }
+
+ event_type_str = libinotify_monitor_event_to_string (event_type);
+ g_message ("Received monitor event:%d->'%s' for file:'%s' (cookie:%d)",
+ event_type,
+ event_type_str,
+ str1 ? str1 : "",
+ cookie);
+ g_free (event_type_str);
+
+#ifdef PAUSE_ON_IO
+ if (monitor->private->unpause_timeout_id != 0) {
+ g_source_remove (monitor->private->unpause_timeout_id);
+ } else {
+ g_message ("Pausing indexing because we are "
+ "receiving monitor events");
+
+ tracker_status_set_is_paused_for_io (TRUE);
+ }
+
+ monitor->private->unpause_timeout_id =
+ g_timeout_add_seconds (PAUSE_ON_IO_SECONDS,
+ unpause_cb,
+ monitor);
+#endif /* PAUSE_ON_IO */
+
+ if (cookie > 0) {
+ /* First check if we already have a file in
+ * the event pairs hash table.
+ */
+ data = g_hash_table_lookup (monitor->private->event_pairs,
+ GUINT_TO_POINTER (cookie));
+
+ if (!data) {
+ data = event_data_new (file, event_type);
+
+ g_hash_table_insert (monitor->private->event_pairs,
+ GUINT_TO_POINTER (cookie),
+ data);
+ } else {
+ other_file = data->file;
+ }
+
+ /* Add a check for old cookies we didn't
+ * receive the follow up pair event for.
+ */
+ if (!monitor->private->event_pairs_timeout_id) {
+ g_debug ("Setting up event pair timeout check");
+
+ monitor->private->event_pairs_timeout_id =
+ g_timeout_add_seconds (CACHE_LIFETIME_SECONDS,
+ libinotify_event_pairs_timeout_cb,
+ monitor);
+ }
+ }
+
+ switch (event_type) {
+ case IN_MODIFY:
+ case IN_CLOSE_WRITE:
+ case IN_ATTRIB:
+ set_up_cache_timeout = TRUE;
+
+ data = g_hash_table_lookup (monitor->private->cached_events, file);
+ if (data) {
+ /* We already have an event we will
+ * signal when we timeout. So update
+ * and don't propagate yet this event.
+ *
+ * See IN_CREATE.
+ */
+ event_data_update (data);
+ break;
+ }
+
+ /* Here we just wait to make sure we don't get
+ * any more MODIFY events and after 2 seconds
+ * of no MODIFY events we emit it. This saves
+ * spamming.
+ */
+ data = event_data_new (file, event_type);
+
+ g_hash_table_insert (monitor->private->cached_events,
+ g_object_ref (data->file),
+ data);
+ break;
+
+ case IN_MOVED_FROM:
+ /* If the file is *ALREADY* in the cache, we need to
+ * handle that cache first. Otherwise we have
+ * disparity when the cache expires.
+ */
+ libinotify_monitor_force_emission (monitor,
+ file,
+ event_type,
+ module_name,
+ is_directory);
+
+ /* Fall through */
+ case IN_DELETE:
+ case IN_DELETE_SELF:
+ case IN_UNMOUNT:
+ if (cookie == 0 || event_type == IN_UNMOUNT) {
+ /* This is how things generally work with
+ * deleted items:
+ *
+ * 1. Check if we have a child file already in
+ * the deleted hash table AND this is a
+ * directory. If so, we delete all child
+ * items.
+ * 2. Add the file to the deleted hash table
+ * but only if we don't have a higher level
+ * directory in the hash table already.
+ * 3. Make sure we have the timeout set up to
+ * handle deleted items after n seconds.
+ * 4. When we handle deleted items, only emit
+ * those which were deleted in the last 2
+ * seconds.
+ */
+
+ data = event_data_new (file, event_type);
+
+ /* Stage 1: */
+ if (is_directory) {
+ gint count;
+
+ tracker_monitor_remove (monitor,
+ module_name,
+ file);
+
+ count = g_hash_table_foreach_remove (monitor->private->cached_events,
+ libinotify_cached_event_delete_children_func,
+ data);
+
+ g_debug ("Removed %d child items in recently deleted cache", count);
+ }
+
+ /* Stage 2: */
+ g_hash_table_insert (monitor->private->cached_events,
+ g_object_ref (data->file),
+ data);
+
+ /* Stage 3: */
+ set_up_cache_timeout = TRUE;
+
+ /* FIXME: How do we handle the deleted items
+ * cache for other events, i.e. we should
+ * have something like
+ * libinotify_monitor_force_emission() for
+ * deleted items if we get CREATED before we
+ * emit the DELETE event.
+ */
+ } else if (other_file) {
+ g_signal_emit (monitor,
+ signals[ITEM_MOVED], 0,
+ module_name,
+ file,
+ other_file,
+ is_directory,
+ TRUE);
+ g_hash_table_remove (monitor->private->event_pairs,
+ GUINT_TO_POINTER (cookie));
+ }
+
+ break;
+
+ case IN_CREATE:
+ /* Here we just wait with CREATE events and
+ * if we get MODIFY after, we drop the MODIFY
+ * and just emit CREATE because otherwise we
+ * send twice as much traffic to the indexer.
+ */
+ set_up_cache_timeout = TRUE;
+
+ data = event_data_new (file, event_type);
+
+ g_hash_table_insert (monitor->private->cached_events,
+ g_object_ref (data->file),
+ data);
+ break;
+
+ case IN_MOVED_TO:
+ /* If the file is *ALREADY* in the cache, we need to
+ * handle that cache first. Otherwise we have
+ * disparity when the cache expires.
+ */
+ libinotify_monitor_force_emission (monitor, file, event_type,
+ module_name, is_directory);
+
+ /* Handle event */
+ if (cookie == 0) {
+ g_signal_emit (monitor,
+ signals[ITEM_CREATED], 0,
+ module_name,
+ file,
+ is_directory);
+ } else if (other_file) {
+ gboolean is_source_indexed;
+
+ /* We check for the event pair in the
+ * hash table here. If it doesn't
+ * exist even though we have a cookie
+ * it means we didn't have a monitor
+ * set up on the source location.
+ * This means we need to get the
+ * processor to crawl the new
+ * location.
+ */
+
+ if (g_hash_table_lookup (monitor->private->event_pairs,
+ GUINT_TO_POINTER (cookie))) {
+ is_source_indexed = TRUE;
+ } else {
+ is_source_indexed = FALSE;
+ }
+
+ g_signal_emit (monitor,
+ signals[ITEM_MOVED], 0,
+ module_name,
+ other_file,
+ file,
+ is_directory,
+ is_source_indexed);
+ g_hash_table_remove (monitor->private->event_pairs,
+ GUINT_TO_POINTER (cookie));
+ }
+
+ break;
+
+ case IN_MOVE_SELF:
+ /* We ignore this one because it is a
+ * convenience state and we handle the
+ * MOVE_TO and MOVE_FROM already.
+ */
+
+ default:
+ break;
+ }
+
+ if (set_up_cache_timeout &&
+ monitor->private->cached_events_timeout_id == 0) {
+ g_debug ("Setting up cached events timeout check");
+
+ monitor->private->cached_events_timeout_id =
+ g_timeout_add_seconds (CACHE_LIFETIME_SECONDS,
+ libinotify_cached_events_timeout_cb,
+ monitor);
+ }
+
+ g_free (str1);
+ g_free (str2);
+ g_object_unref (file);
+}
+
+static INotifyHandle *
+libinotify_monitor_directory (TrackerMonitor *monitor,
+ GFile *file)
+{
+ INotifyHandle *handle;
+ gchar *path;
+ guint32 mask;
+ unsigned long flags;
+
+ flags = 0;
+ flags &= ~IN_FLAG_FILE_BASED;
+ mask = IN_ALL_EVENTS;
+
+ /* For files */
+ /* flags |= IN_FLAG_FILE_BASED; */
+
+ path = g_file_get_path (file);
+ handle = inotify_monitor_add (path,
+ mask,
+ flags,
+ libinotify_monitor_event_cb,
+ monitor);
+ g_free (path);
+
+ if (!handle) {
+ return NULL;
+ }
+
+ return handle;
+}
+
+static void
+libinotify_monitor_cancel (gpointer data)
+{
+ inotify_monitor_remove (data);
+}
+
+TrackerMonitor *
+tracker_monitor_new (TrackerConfig *config)
+{
+ TrackerMonitor *monitor;
+ TrackerMonitorPrivate *priv;
+
+ g_return_val_if_fail (TRACKER_IS_CONFIG (config), NULL);
+
+ monitor = g_object_new (TRACKER_TYPE_MONITOR, NULL);
+
+ priv = monitor->private;
+
+ priv->config = g_object_ref (config);
+
+ return monitor;
+}
+
+gboolean
+tracker_monitor_get_enabled (TrackerMonitor *monitor)
+{
+ g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), FALSE);
+
+ return monitor->private->enabled;
+}
+
+void
+tracker_monitor_set_enabled (TrackerMonitor *monitor,
+ gboolean enabled)
+{
+ g_return_if_fail (TRACKER_IS_MONITOR (monitor));
+
+ monitor->private->enabled = enabled;
+
+ g_object_notify (G_OBJECT (monitor), "enabled");
+}
+
+gboolean
+tracker_monitor_add (TrackerMonitor *monitor,
+ const gchar *module_name,
+ GFile *file)
+{
+ INotifyHandle *file_monitor;
+ GHashTable *monitors;
+ GSList *ignored_roots;
+ GSList *l;
+ gchar *path;
+
+ g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), FALSE);
+ g_return_val_if_fail (module_name != NULL, FALSE);
+ g_return_val_if_fail (G_IS_FILE (file), FALSE);
+
+ if (!tracker_config_get_enable_watches (monitor->private->config)) {
+ return TRUE;
+ }
+
+ monitors = g_hash_table_lookup (monitor->private->modules, module_name);
+ if (!monitors) {
+ g_warning ("No monitor hash table for module:'%s'",
+ module_name);
+ return FALSE;
+ }
+
+ if (g_hash_table_lookup (monitors, file)) {
+ return TRUE;
+ }
+
+ /* Cap the number of monitors */
+ if (g_hash_table_size (monitors) >= monitor->private->monitor_limit) {
+ monitor->private->monitors_ignored++;
+
+ if (!monitor->private->monitor_limit_warned) {
+ g_warning ("The maximum number of monitors to set (%d) "
+ "has been reached, not adding any new ones",
+ monitor->private->monitor_limit);
+ monitor->private->monitor_limit_warned = TRUE;
+ }
+
+ return FALSE;
+ }
+
+ path = g_file_get_path (file);
+
+ ignored_roots = tracker_config_get_no_watch_directory_roots (monitor->private->config);
+
+ /* Check this location isn't excluded in the config */
+ for (l = ignored_roots; l; l = l->next) {
+ if (strcmp (path, l->data) == 0) {
+ g_message ("Not adding monitor for:'%s', path is in config ignore list",
+ path);
+ g_free (path);
+ return FALSE;
+ }
+ }
+
+ /* 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.
+ */
+ file_monitor = libinotify_monitor_directory (monitor, file);
+
+ if (!file_monitor) {
+ g_warning ("Could not add monitor for path:'%s'",
+ path);
+ g_free (path);
+ return FALSE;
+ }
+
+ g_hash_table_insert (monitors,
+ g_object_ref (file),
+ file_monitor);
+
+ g_debug ("Added monitor for module:'%s', path:'%s', total monitors:%d",
+ module_name,
+ path,
+ g_hash_table_size (monitors));
+
+ g_free (path);
+
+ return TRUE;
+}
+
+gboolean
+tracker_monitor_remove (TrackerMonitor *monitor,
+ const gchar *module_name,
+ GFile *file)
+{
+ GHashTable *monitors;
+ gchar *path;
+ gboolean removed;
+
+ g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), FALSE);
+ g_return_val_if_fail (module_name != NULL, FALSE);
+ g_return_val_if_fail (G_IS_FILE (file), FALSE);
+
+ monitors = g_hash_table_lookup (monitor->private->modules, module_name);
+ if (!monitors) {
+ g_warning ("No monitor hash table for module:'%s'",
+ module_name);
+ return FALSE;
+ }
+
+ removed = g_hash_table_remove (monitors, file);
+ path = g_file_get_path (file);
+
+ g_debug ("Removed monitor for module:'%s', path:'%s', total monitors:%d",
+ module_name,
+ path,
+ g_hash_table_size (monitors));
+
+ g_free (path);
+
+ /* tracker_monitor_remove_recursively (monitor, event->file); */
+
+ return removed;
+}
+
+
+gboolean
+tracker_monitor_remove_recursively (TrackerMonitor *monitor,
+ GFile *file)
+{
+ GHashTableIter iter1;
+ gpointer iter_module_name, iter_hash_table;
+ guint items_removed = 0;
+
+ g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), FALSE);
+ g_return_val_if_fail (G_IS_FILE (file), FALSE);
+
+ g_hash_table_iter_init (&iter1, monitor->private->modules);
+ while (g_hash_table_iter_next (&iter1, &iter_module_name, &iter_hash_table)) {
+ GHashTableIter iter2;
+ gpointer iter_file, iter_file_monitor;
+
+ g_hash_table_iter_init (&iter2, iter_hash_table);
+ while (g_hash_table_iter_next (&iter2, &iter_file, &iter_file_monitor)) {
+ gchar *path;
+
+ if (!g_file_has_prefix (iter_file, file) &&
+ !g_file_equal (iter_file, file)) {
+ continue;
+ }
+
+ path = g_file_get_path (iter_file);
+
+ g_debug ("Removed monitor for module:'%s', path:'%s', total monitors:%d",
+ (gchar*) iter_module_name,
+ path,
+ g_hash_table_size (iter_hash_table));
+
+ g_free (path);
+
+ g_hash_table_iter_remove (&iter2);
+
+ /* We reset this because now it is possible we have limit - 1 */
+ monitor->private->monitor_limit_warned = FALSE;
+
+ items_removed++;
+ }
+ }
+
+ return items_removed > 0;
+}
+
+gboolean
+tracker_monitor_is_watched (TrackerMonitor *monitor,
+ const gchar *module_name,
+ GFile *file)
+{
+ GHashTable *monitors;
+
+ g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), FALSE);
+ g_return_val_if_fail (module_name != NULL, FALSE);
+ g_return_val_if_fail (G_IS_FILE (file), FALSE);
+
+ monitors = g_hash_table_lookup (monitor->private->modules, module_name);
+ if (!monitors) {
+ g_warning ("No monitor hash table for module:'%s'",
+ module_name);
+ return FALSE;
+ }
+
+ return g_hash_table_lookup (monitors, file) != NULL;
+}
+
+gboolean
+tracker_monitor_is_watched_by_string (TrackerMonitor *monitor,
+ const gchar *module_name,
+ const gchar *path)
+{
+ GFile *file;
+ GHashTable *monitors;
+ gboolean watched;
+
+ g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), FALSE);
+ g_return_val_if_fail (module_name != NULL, FALSE);
+ g_return_val_if_fail (path != NULL, FALSE);
+
+ monitors = g_hash_table_lookup (monitor->private->modules, module_name);
+ if (!monitors) {
+ g_warning ("No monitor hash table for module:'%s'",
+ module_name);
+ return FALSE;
+ }
+
+ file = g_file_new_for_path (path);
+ watched = g_hash_table_lookup (monitors, file) != NULL;
+ g_object_unref (file);
+
+ return watched;
+}
+
+guint
+tracker_monitor_get_count (TrackerMonitor *monitor,
+ const gchar *module_name)
+{
+ guint count;
+
+ g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), 0);
+
+ if (module_name) {
+ GHashTable *monitors;
+
+ monitors = g_hash_table_lookup (monitor->private->modules, module_name);
+ if (!monitors) {
+ g_warning ("No monitor hash table for module:'%s'",
+ module_name);
+ return 0;
+ }
+
+ count = g_hash_table_size (monitors);
+ } else {
+ GList *all_modules, *l;
+
+ all_modules = g_hash_table_get_values (monitor->private->modules);
+
+ for (l = all_modules, count = 0; l; l = l->next) {
+ count += g_hash_table_size (l->data);
+ }
+
+ g_list_free (all_modules);
+ }
+
+ return count;
+}
+
+guint
+tracker_monitor_get_ignored (TrackerMonitor *monitor)
+{
+ g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), 0);
+
+ return monitor->private->monitors_ignored;
+}
diff --git a/src/libtracker-miner/tracker-monitor.h b/src/libtracker-miner/tracker-monitor.h
new file mode 100644
index 0000000..c456988
--- /dev/null
+++ b/src/libtracker-miner/tracker-monitor.h
@@ -0,0 +1,77 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library 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 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#ifndef __TRACKERD_MONITOR_H__
+#define __TRACKERD_MONITOR_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "tracker-config.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))
+
+typedef struct _TrackerMonitor TrackerMonitor;
+typedef struct _TrackerMonitorClass TrackerMonitorClass;
+typedef struct _TrackerMonitorPrivate TrackerMonitorPrivate;
+
+struct _TrackerMonitor {
+ GObject parent;
+ TrackerMonitorPrivate *private;
+};
+
+struct _TrackerMonitorClass {
+ GObjectClass parent;
+};
+
+GType tracker_monitor_get_type (void);
+TrackerMonitor *tracker_monitor_new (TrackerConfig *config);
+gboolean tracker_monitor_get_enabled (TrackerMonitor *monitor);
+void tracker_monitor_set_enabled (TrackerMonitor *monitor,
+ gboolean enabled);
+gboolean tracker_monitor_add (TrackerMonitor *monitor,
+ const gchar *module_name,
+ GFile *file);
+gboolean tracker_monitor_remove (TrackerMonitor *monitor,
+ const gchar *module_name,
+ GFile *file);
+gboolean tracker_monitor_remove_recursively (TrackerMonitor *monitor,
+ GFile *file);
+gboolean tracker_monitor_is_watched (TrackerMonitor *monitor,
+ const gchar *module_name,
+ GFile *file);
+gboolean tracker_monitor_is_watched_by_string (TrackerMonitor *monitor,
+ const gchar *module_name,
+ const gchar *path);
+guint tracker_monitor_get_count (TrackerMonitor *monitor,
+ const gchar *module_name);
+guint tracker_monitor_get_ignored (TrackerMonitor *monitor);
+
+G_END_DECLS
+
+#endif /* __TRACKERD_MONITOR_H__ */
diff --git a/src/libtracker-miner/tracker-processor.c b/src/libtracker-miner/tracker-processor.c
new file mode 100644
index 0000000..663d14b
--- /dev/null
+++ b/src/libtracker-miner/tracker-processor.c
@@ -0,0 +1,1774 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+ *
+ * This library 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 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.h"
+
+#include <string.h>
+
+#include <libtracker-common/tracker-dbus.h>
+#include <libtracker-common/tracker-file-utils.h>
+#include <libtracker-common/tracker-storage.h>
+#include <libtracker-common/tracker-module-config.h>
+#include <libtracker-common/tracker-utils.h>
+
+#include <libtracker-db/tracker-db-manager.h>
+
+#include "tracker-crawler.h"
+#include "tracker-dbus.h"
+#include "tracker-monitor.h"
+#include "tracker-processor.h"
+#include "tracker-status.h"
+
+#define TRACKER_PROCESSOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRACKER_TYPE_PROCESSOR, TrackerProcessorPrivate))
+
+typedef enum {
+ SENT_TYPE_NONE,
+ SENT_TYPE_CREATED,
+ SENT_TYPE_UPDATED,
+ SENT_TYPE_DELETED,
+ SENT_TYPE_MOVED
+} SentType;
+
+struct TrackerProcessorPrivate {
+ TrackerConfig *config;
+ TrackerStorage *hal;
+ TrackerMonitor *monitor;
+
+ /* Crawlers */
+ GHashTable *crawlers;
+
+ /* File queues for indexer */
+ guint item_queues_handler_id;
+
+ GHashTable *items_created_queues;
+ GHashTable *items_updated_queues;
+ GHashTable *items_deleted_queues;
+ GHashTable *items_moved_queues;
+
+ SentType sent_type;
+ GStrv sent_items;
+ const gchar *sent_module_name;
+
+ /* Status */
+ GList *modules;
+ GList *current_module;
+
+ GList *devices;
+ GList *current_device;
+
+ GTimer *timer;
+
+ gboolean been_started;
+ gboolean interrupted;
+
+ gboolean finished_modules;
+ gboolean finished_devices;
+ gboolean finished_sending;
+ gboolean finished_indexer;
+
+ /* Statistics */
+ guint total_directories_found;
+ guint total_directories_ignored;
+ guint total_files_found;
+ guint total_files_ignored;
+
+ guint directories_found;
+ guint directories_ignored;
+ guint files_found;
+ guint files_ignored;
+};
+
+enum {
+ FINISHED,
+ LAST_SIGNAL
+};
+
+static void tracker_processor_finalize (GObject *object);
+static void crawler_destroy_notify (gpointer data);
+static void item_queue_destroy_notify (gpointer data);
+static void process_module_next (TrackerProcessor *processor);
+static void process_modules_stop (TrackerProcessor *processor);
+static void process_device_next (TrackerProcessor *processor);
+static void process_devices_stop (TrackerProcessor *processor);
+static void process_check_completely_finished (TrackerProcessor *processor);
+static void process_next (TrackerProcessor *processor);
+#if 0
+static void indexer_started_cb (TrackerIndexer *indexer,
+ gpointer user_data);
+static void indexer_finished_cb (TrackerIndexer *indexer,
+ gdouble seconds_elapsed,
+ guint items_processed,
+ guint items_indexed,
+ gboolean interrupted,
+ gpointer user_data);
+#endif
+static void monitor_item_created_cb (TrackerMonitor *monitor,
+ const gchar *module_name,
+ GFile *file,
+ gboolean is_directory,
+ gpointer user_data);
+static void monitor_item_updated_cb (TrackerMonitor *monitor,
+ const gchar *module_name,
+ GFile *file,
+ gboolean is_directory,
+ gpointer user_data);
+static void monitor_item_deleted_cb (TrackerMonitor *monitor,
+ const gchar *module_name,
+ GFile *file,
+ gboolean is_directory,
+ gpointer user_data);
+static void monitor_item_moved_cb (TrackerMonitor *monitor,
+ const gchar *module_name,
+ GFile *file,
+ GFile *other_file,
+ gboolean is_directory,
+ gboolean is_source_monitored,
+ gpointer user_data);
+static void crawler_processing_file_cb (TrackerCrawler *crawler,
+ const gchar *module_name,
+ GFile *file,
+ gpointer user_data);
+static void crawler_processing_directory_cb (TrackerCrawler *crawler,
+ const gchar *module_name,
+ GFile *file,
+ gpointer user_data);
+static void crawler_finished_cb (TrackerCrawler *crawler,
+ const gchar *module_name,
+ guint directories_found,
+ guint directories_ignored,
+ guint files_found,
+ guint files_ignored,
+ gpointer user_data);
+#ifdef HAVE_HAL
+static void mount_point_added_cb (TrackerStorage *hal,
+ const gchar *volume_uuid,
+ const gchar *mount_point,
+ gpointer user_data);
+static void mount_point_removed_cb (TrackerStorage *hal,
+ const gchar *volume_uuid,
+ const gchar *mount_point,
+ gpointer user_data);
+#endif /* HAVE_HAL */
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (TrackerProcessor, tracker_processor, G_TYPE_OBJECT)
+
+static void
+tracker_processor_class_init (TrackerProcessorClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ object_class->finalize = tracker_processor_finalize;
+
+ signals[FINISHED] =
+ g_signal_new ("finished",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (TrackerProcessorClass, finished),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ g_type_class_add_private (object_class, sizeof (TrackerProcessorPrivate));
+}
+
+static void
+tracker_processor_init (TrackerProcessor *object)
+{
+ TrackerProcessorPrivate *priv;
+ GList *l;
+
+ object->private = TRACKER_PROCESSOR_GET_PRIVATE (object);
+
+ priv = object->private;
+
+ priv->modules = tracker_module_config_get_modules ();
+
+ /* For each module we create a TrackerCrawler and keep them in
+ * a hash table to look up.
+ */
+ priv->crawlers =
+ g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ crawler_destroy_notify);
+
+
+ /* For each module we create a hash table for queues for items
+ * to update/create/delete in the indexer. This is sent on
+ * when the queue is processed.
+ */
+ priv->items_created_queues =
+ g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ item_queue_destroy_notify);
+ priv->items_updated_queues =
+ g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ item_queue_destroy_notify);
+ priv->items_deleted_queues =
+ g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ item_queue_destroy_notify);
+ priv->items_moved_queues =
+ g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ item_queue_destroy_notify);
+
+ for (l = priv->modules; l; l = l->next) {
+ /* Create queues for this module */
+ g_hash_table_insert (priv->items_created_queues,
+ g_strdup (l->data),
+ g_queue_new ());
+ g_hash_table_insert (priv->items_updated_queues,
+ g_strdup (l->data),
+ g_queue_new ());
+ g_hash_table_insert (priv->items_deleted_queues,
+ g_strdup (l->data),
+ g_queue_new ());
+ g_hash_table_insert (priv->items_moved_queues,
+ g_strdup (l->data),
+ g_queue_new ());
+ }
+}
+
+static void
+tracker_processor_finalize (GObject *object)
+{
+ TrackerProcessorPrivate *priv;
+
+ priv = TRACKER_PROCESSOR_GET_PRIVATE (object);
+
+ if (priv->timer) {
+ g_timer_destroy (priv->timer);
+ }
+
+ if (priv->item_queues_handler_id) {
+ g_source_remove (priv->item_queues_handler_id);
+ priv->item_queues_handler_id = 0;
+ }
+
+ if (priv->items_moved_queues) {
+ g_hash_table_unref (priv->items_moved_queues);
+ }
+
+ if (priv->items_deleted_queues) {
+ g_hash_table_unref (priv->items_deleted_queues);
+ }
+
+ if (priv->items_updated_queues) {
+ g_hash_table_unref (priv->items_updated_queues);
+ }
+
+ if (priv->items_created_queues) {
+ g_hash_table_unref (priv->items_created_queues);
+ }
+
+ if (priv->crawlers) {
+ g_hash_table_unref (priv->crawlers);
+ }
+
+ g_list_free (priv->modules);
+
+#if 0
+ g_signal_handlers_disconnect_by_func (priv->indexer,
+ G_CALLBACK (indexer_started_cb),
+ NULL);
+ g_signal_handlers_disconnect_by_func (priv->indexer,
+ G_CALLBACK (indexer_finished_cb),
+ NULL);
+#endif
+
+ g_signal_handlers_disconnect_by_func (priv->monitor,
+ G_CALLBACK (monitor_item_deleted_cb),
+ object);
+ g_signal_handlers_disconnect_by_func (priv->monitor,
+ G_CALLBACK (monitor_item_updated_cb),
+ object);
+ g_signal_handlers_disconnect_by_func (priv->monitor,
+ G_CALLBACK (monitor_item_created_cb),
+ object);
+ g_signal_handlers_disconnect_by_func (priv->monitor,
+ G_CALLBACK (monitor_item_moved_cb),
+ object);
+ g_object_unref (priv->monitor);
+
+#ifdef HAVE_HAL
+ if (priv->devices) {
+ g_list_foreach (priv->devices, (GFunc) g_free, NULL);
+ g_list_free (priv->devices);
+ }
+
+ if (priv->hal) {
+ g_signal_handlers_disconnect_by_func (priv->hal,
+ mount_point_added_cb,
+ object);
+ g_signal_handlers_disconnect_by_func (priv->hal,
+ mount_point_removed_cb,
+ object);
+
+ g_object_unref (priv->hal);
+ }
+#endif /* HAVE_HAL */
+
+ g_object_unref (priv->config);
+
+ G_OBJECT_CLASS (tracker_processor_parent_class)->finalize (object);
+}
+
+
+static void
+get_remote_roots (TrackerProcessor *processor,
+ GList **mounted_directory_roots,
+ GList **removable_device_roots)
+{
+ GList *l1;
+ GList *l2;
+
+#ifdef HAVE_HAL
+ l1 = tracker_storage_get_mounted_directory_roots (processor->private->hal);
+ l2 = tracker_storage_get_removable_device_roots (processor->private->hal);
+#else /* HAVE_HAL */
+ l1 = NULL;
+ l2 = NULL;
+#endif /* HAVE_HAL */
+
+ /* The options to index removable media and the index mounted
+ * directories are both mutually exclusive even though
+ * removable media is mounted on a directory.
+ *
+ * Since we get ALL mounted directories from HAL, we need to
+ * remove those which are removable device roots.
+ */
+ if (l2) {
+ GList *l;
+ GList *list = NULL;
+
+ for (l = l1; l; l = l->next) {
+ if (g_list_find_custom (l2, l->data, (GCompareFunc) g_strcmp0)) {
+ continue;
+ }
+
+ list = g_list_prepend (list, l->data);
+ }
+
+ *mounted_directory_roots = g_list_reverse (list);
+ } else {
+ *mounted_directory_roots = NULL;
+ }
+
+ *removable_device_roots = g_list_copy (l2);
+}
+
+static gboolean
+path_should_be_ignored_for_media (TrackerProcessor *processor,
+ const gchar *path)
+{
+ GList *roots = NULL;
+ GList *mounted_directory_roots = NULL;
+ GList *removable_device_roots = NULL;
+ GList *l;
+ gboolean ignore_mounted_directories;
+ gboolean ignore_removable_devices;
+ gboolean ignore = FALSE;
+
+ ignore_mounted_directories =
+ !tracker_config_get_index_mounted_directories (processor->private->config);
+ ignore_removable_devices =
+ !tracker_config_get_index_removable_devices (processor->private->config);
+
+ if (ignore_mounted_directories || ignore_removable_devices) {
+ get_remote_roots (processor,
+ &mounted_directory_roots,
+ &removable_device_roots);
+ }
+
+ if (ignore_mounted_directories) {
+ roots = g_list_concat (roots, mounted_directory_roots);
+ }
+
+ if (ignore_removable_devices) {
+ roots = g_list_concat (roots, removable_device_roots);
+ }
+
+ for (l = roots; l && !ignore; l = l->next) {
+ /* If path matches a mounted or removable device by
+ * prefix then we should ignore it since we don't
+ * crawl those by choice in the config.
+ */
+ if (strcmp (path, l->data) == 0) {
+ ignore = TRUE;
+ }
+
+ /* FIXME: Should we add a DIR_SEPARATOR on the end of
+ * these before comparing them?
+ */
+ if (g_str_has_prefix (path, l->data)) {
+ ignore = TRUE;
+ }
+ }
+
+ g_list_free (roots);
+
+ return ignore;
+}
+
+static GQueue *
+get_next_queue_with_data (GList *modules,
+ GHashTable *hash_table,
+ gchar **module_name)
+{
+ GQueue *found_queue;
+ GQueue *queue;
+ GList *l;
+
+ if (module_name) {
+ *module_name = NULL;
+ }
+
+ for (l = modules, found_queue = NULL; l && !found_queue; l = l->next) {
+ queue = g_hash_table_lookup (hash_table, l->data);
+
+ if (g_queue_get_length (queue) > 0) {
+ if (module_name) {
+ *module_name = l->data;
+ found_queue = queue;
+ }
+ }
+ }
+
+ return found_queue;
+}
+
+static void
+crawler_destroy_notify (gpointer data)
+{
+ TrackerCrawler *crawler;
+
+ crawler = TRACKER_CRAWLER (data);
+
+ if (crawler) {
+ guint lsignals;
+
+ lsignals = g_signal_handlers_disconnect_matched (crawler,
+ G_SIGNAL_MATCH_FUNC,
+ 0,
+ 0,
+ NULL,
+ G_CALLBACK (crawler_processing_file_cb),
+ NULL);
+ lsignals = g_signal_handlers_disconnect_matched (crawler,
+ G_SIGNAL_MATCH_FUNC,
+ 0,
+ 0,
+ NULL,
+ G_CALLBACK (crawler_processing_directory_cb),
+ NULL);
+ lsignals = g_signal_handlers_disconnect_matched (crawler,
+ G_SIGNAL_MATCH_FUNC,
+ 0,
+ 0,
+ NULL,
+ G_CALLBACK (crawler_finished_cb),
+ NULL);
+
+ g_object_unref (crawler);
+ }
+}
+
+static void
+item_queue_destroy_notify (gpointer data)
+{
+ GQueue *queue;
+
+ queue = (GQueue *) data;
+
+ g_queue_foreach (queue, (GFunc) g_object_unref, NULL);
+ g_queue_free (queue);
+}
+
+static void
+item_queue_processed_cb (TrackerProcessor *processor)
+{
+ g_strfreev (processor->private->sent_items);
+
+ /* Reset for next batch to be sent */
+ processor->private->sent_items = NULL;
+ processor->private->sent_module_name = NULL;
+ processor->private->sent_type = SENT_TYPE_NONE;
+}
+
+static gboolean
+item_queue_handlers_cb (gpointer user_data)
+{
+ TrackerProcessor *processor;
+ TrackerStatus status;
+ GQueue *queue;
+ GStrv files;
+ gchar *module_name;
+ gboolean should_repeat = FALSE;
+ GTimeVal time_now;
+ static GTimeVal time_last = { 0, 0 };
+
+ processor = user_data;
+
+ status = tracker_status_get ();
+
+ /* Don't spam */
+ g_get_current_time (&time_now);
+
+ should_repeat = (time_now.tv_sec - time_last.tv_sec) >= 10;
+ if (should_repeat) {
+ time_last = time_now;
+ }
+
+ switch (status) {
+ case TRACKER_STATUS_PAUSED:
+ /* This way we don't send anything to the indexer from
+ * monitor events but we still queue them ready to
+ * send when we are unpaused.
+ */
+ if (should_repeat) {
+ g_message ("We are paused, sending nothing to the "
+ "indexer until we are unpaused");
+ }
+
+ case TRACKER_STATUS_PENDING:
+ case TRACKER_STATUS_WATCHING:
+ /* Wait until we have finished crawling before
+ * sending anything.
+ */
+ return TRUE;
+
+ default:
+ break;
+ }
+
+ /* This is here so we don't try to send something if we are
+ * still waiting for a response from the last send.
+ */
+ if (processor->private->sent_type != SENT_TYPE_NONE) {
+ if (should_repeat) {
+ g_message ("Still waiting for response from indexer, "
+ "not sending more files yet");
+ }
+
+ return TRUE;
+ }
+
+ /* Process the deleted items first */
+ queue = get_next_queue_with_data (processor->private->modules,
+ processor->private->items_deleted_queues,
+ &module_name);
+
+ if (queue) {
+ files = tracker_dbus_queue_gfile_to_strv (queue, -1);
+
+ g_message ("Queue for module:'%s' deleted items processed, sending %d to the indexer",
+ module_name,
+ g_strv_length (files));
+
+ processor->private->finished_indexer = FALSE;
+
+ processor->private->sent_type = SENT_TYPE_DELETED;
+ processor->private->sent_module_name = module_name;
+ processor->private->sent_items = files;
+
+#if 0
+ tracker_indexer_files_check (processor->private->indexer,
+ module_name,
+ files);
+#endif
+ item_queue_processed_cb (processor);
+
+ return TRUE;
+ }
+
+ /* Process the created items next */
+ queue = get_next_queue_with_data (processor->private->modules,
+ processor->private->items_created_queues,
+ &module_name);
+
+ if (queue) {
+ /* Now we try to send items to the indexer */
+ tracker_status_set_and_signal (TRACKER_STATUS_INDEXING);
+
+ files = tracker_dbus_queue_gfile_to_strv (queue, -1);
+
+ g_message ("Queue for module:'%s' created items processed, sending %d to the indexer",
+ module_name,
+ g_strv_length (files));
+
+ processor->private->finished_indexer = FALSE;
+
+ processor->private->sent_type = SENT_TYPE_CREATED;
+ processor->private->sent_module_name = module_name;
+ processor->private->sent_items = files;
+
+#if 0
+ tracker_indexer_files_check (processor->private->indexer,
+ module_name,
+ files);
+#endif
+ item_queue_processed_cb (processor);
+
+ return TRUE;
+ }
+
+ /* Process the updated items next */
+ queue = get_next_queue_with_data (processor->private->modules,
+ processor->private->items_updated_queues,
+ &module_name);
+
+ if (queue) {
+ /* Now we try to send items to the indexer */
+ tracker_status_set_and_signal (TRACKER_STATUS_INDEXING);
+
+ files = tracker_dbus_queue_gfile_to_strv (queue, -1);
+
+ g_message ("Queue for module:'%s' updated items processed, sending %d to the indexer",
+ module_name,
+ g_strv_length (files));
+
+ processor->private->finished_indexer = FALSE;
+
+ processor->private->sent_type = SENT_TYPE_UPDATED;
+ processor->private->sent_module_name = module_name;
+ processor->private->sent_items = files;
+
+#if 0
+ tracker_indexer_files_check (processor->private->indexer,
+ module_name,
+ files);
+#endif
+ item_queue_processed_cb (processor);
+
+ return TRUE;
+ }
+
+ /* Process the moved items last */
+ queue = get_next_queue_with_data (processor->private->modules,
+ processor->private->items_moved_queues,
+ &module_name);
+
+ if (queue) {
+ const gchar *source;
+ const gchar *target;
+
+ /* Now we try to send items to the indexer */
+ tracker_status_set_and_signal (TRACKER_STATUS_INDEXING);
+
+ files = tracker_dbus_queue_gfile_to_strv (queue, 2);
+
+ if (files) {
+ source = files[0];
+ target = files[1];
+ } else {
+ source = NULL;
+ target = NULL;
+ }
+
+ g_message ("Queue for module:'%s' moved items processed, sending %d to the indexer",
+ module_name,
+ g_strv_length (files));
+
+ processor->private->finished_indexer = FALSE;
+
+ processor->private->sent_type = SENT_TYPE_MOVED;
+ processor->private->sent_module_name = module_name;
+ processor->private->sent_items = files;
+
+#if 0
+ tracker_indexer_file_move (processor->private->indexer,
+ module_name,
+ source,
+ target);
+#endif
+ item_queue_processed_cb (processor);
+
+ return TRUE;
+ }
+
+ processor->private->item_queues_handler_id = 0;
+
+ processor->private->finished_sending = TRUE;
+ process_check_completely_finished (processor);
+
+ return FALSE;
+}
+
+static void
+item_queue_handlers_set_up (TrackerProcessor *processor)
+{
+ processor->private->finished_sending = FALSE;
+
+ if (processor->private->item_queues_handler_id != 0) {
+ return;
+ }
+
+ processor->private->item_queues_handler_id =
+ g_idle_add (item_queue_handlers_cb,
+ processor);
+}
+
+static gboolean
+is_path_on_ignore_list (GSList *ignore_list,
+ const gchar *path)
+{
+ GSList *l;
+
+ if (!ignore_list || !path) {
+ return FALSE;
+ }
+
+ for (l = ignore_list; l; l = l->next) {
+ if (strcmp (path, l->data) == 0) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+process_module_files_add_legacy_options (TrackerProcessor *processor)
+{
+ TrackerCrawler *crawler;
+ GSList *no_watch_roots;
+ GSList *watch_roots;
+ GSList *crawl_roots;
+ GSList *l;
+ guint watch_root_count;
+ guint crawl_root_count;
+ const gchar *module_name = "files";
+
+ crawler = g_hash_table_lookup (processor->private->crawlers, module_name);
+
+ tracker_crawler_use_module_paths (crawler, TRUE);
+ tracker_crawler_special_paths_clear (crawler);
+
+ no_watch_roots = tracker_config_get_no_watch_directory_roots (processor->private->config);
+ watch_roots = tracker_config_get_watch_directory_roots (processor->private->config);
+ crawl_roots = tracker_config_get_crawl_directory_roots (processor->private->config);
+
+ watch_root_count = 0;
+ crawl_root_count = 0;
+
+ /* This module get special treatment to make sure legacy
+ * options are supported.
+ */
+
+ /* Print ignored locations */
+ g_message (" User ignored crawls & monitors:");
+ for (l = no_watch_roots; l; l = l->next) {
+ g_message (" %s", (gchar*) l->data);
+ }
+
+ if (g_slist_length (no_watch_roots) == 0) {
+ g_message (" NONE");
+ }
+
+ /* Add monitors, from WatchDirectoryRoots config key */
+ g_message (" User monitors being added:");
+ for (l = watch_roots; l; l = l->next) {
+ GFile *file;
+
+ if (is_path_on_ignore_list (no_watch_roots, l->data)) {
+ continue;
+ }
+
+ g_message (" %s", (gchar*) l->data);
+
+ file = g_file_new_for_path (l->data);
+ tracker_monitor_add (processor->private->monitor, module_name, file);
+ g_object_unref (file);
+
+ watch_root_count++;
+ }
+
+ if (g_slist_length (watch_roots) == 0) {
+ g_message (" NONE");
+ }
+
+ /* Add crawls, from WatchDirectoryRoots and
+ * CrawlDirectoryRoots config keys.
+ */
+ g_message (" User crawls being added:");
+
+ for (l = watch_roots; l; l = l->next) {
+ if (is_path_on_ignore_list (no_watch_roots, l->data)) {
+ continue;
+ }
+
+ g_message (" %s", (gchar*) l->data);
+ tracker_crawler_special_paths_add (crawler, l->data);
+ }
+
+ for (l = crawl_roots; l; l = l->next) {
+ if (is_path_on_ignore_list (no_watch_roots, l->data)) {
+ continue;
+ }
+
+ g_message (" %s", (gchar*) l->data);
+ tracker_crawler_special_paths_add (crawler, l->data);
+
+ crawl_root_count++;
+ }
+
+ if (g_slist_length (watch_roots) == 0 &&
+ g_slist_length (crawl_roots) == 0) {
+ g_message (" NONE");
+ }
+}
+
+static gboolean
+process_module_is_disabled (TrackerProcessor *processor,
+ const gchar *module_name)
+{
+ GSList *disabled_modules;
+
+ if (!tracker_module_config_get_enabled (module_name)) {
+ g_message (" Module disabled by module config");
+ return TRUE;
+ }
+
+ disabled_modules = tracker_config_get_disabled_modules (processor->private->config);
+
+ if (g_slist_find_custom (disabled_modules, module_name, (GCompareFunc) g_strcmp0)) {
+ g_message (" Module disabled by user");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+process_module (TrackerProcessor *processor,
+ const gchar *module_name)
+{
+ TrackerCrawler *crawler;
+
+ g_message ("Processing module:'%s'", module_name);
+
+ if (process_module_is_disabled (processor, module_name)) {
+ process_module_next (processor);
+ return;
+ }
+
+ /* Here we set up legacy .cfg options like watch roots */
+ tracker_status_set_and_signal (TRACKER_STATUS_WATCHING);
+
+ if (strcmp (module_name, "files") == 0) {
+ process_module_files_add_legacy_options (processor);
+ }
+
+ /* Gets all files and directories */
+ tracker_status_set_and_signal (TRACKER_STATUS_PENDING);
+
+ crawler = g_hash_table_lookup (processor->private->crawlers, module_name);
+
+ if (!tracker_crawler_start (crawler)) {
+ process_module_next (processor);
+ }
+}
+
+static void
+process_module_next (TrackerProcessor *processor)
+{
+ if (tracker_status_get_is_readonly ()) {
+ /* Block any request to process
+ * modules if indexing is not enabled
+ */
+ return;
+ }
+
+ /* Don't recursively iterate the modules */
+ if (!processor->private->current_module) {
+ if (!processor->private->finished_modules) {
+ processor->private->current_module = processor->private->modules;
+ }
+ } else {
+ processor->private->current_module = processor->private->current_module->next;
+ }
+
+ /* If we have no further modules to iterate */
+ if (!processor->private->current_module) {
+ process_modules_stop (processor);
+ process_next (processor);
+ return;
+ }
+
+ process_module (processor, processor->private->current_module->data);
+}
+
+static void
+process_device (TrackerProcessor *processor,
+ const gchar *device_root)
+{
+ TrackerCrawler *crawler;
+ GFile *file;
+ const gchar *module_name = "files";
+
+ g_message ("Processing device with root:'%s'", device_root);
+
+ if (process_module_is_disabled (processor, module_name)) {
+ process_device_next (processor);
+ return;
+ }
+
+ /* Here we set up legacy .cfg options like watch roots */
+ tracker_status_set_and_signal (TRACKER_STATUS_WATCHING);
+
+ /* Gets all files and directories */
+ tracker_status_set_and_signal (TRACKER_STATUS_PENDING);
+
+ crawler = g_hash_table_lookup (processor->private->crawlers, module_name);
+
+ tracker_crawler_use_module_paths (crawler, FALSE);
+ tracker_crawler_special_paths_clear (crawler);
+
+ if (path_should_be_ignored_for_media (processor, device_root)) {
+ g_message (" Ignored due to config");
+ process_device_next (processor);
+ return;
+ }
+
+ file = g_file_new_for_path (device_root);
+ tracker_monitor_add (processor->private->monitor, module_name, file);
+ g_object_unref (file);
+
+ tracker_crawler_special_paths_add (crawler, device_root);
+
+ if (!tracker_crawler_start (crawler)) {
+ process_device_next (processor);
+ }
+}
+
+static void
+process_device_next (TrackerProcessor *processor)
+{
+ if (tracker_status_get_is_readonly ()) {
+ /* Block any request to process
+ * modules if indexing is not enabled
+ */
+ return;
+ }
+
+ /* Don't recursively iterate the devices */
+ if (!processor->private->current_device) {
+ if (!processor->private->finished_devices) {
+ processor->private->current_device = processor->private->devices;
+ }
+ } else {
+ GList *l;
+
+ l = processor->private->current_device;
+
+ /* Now free that device so we don't recrawl it */
+ if (l) {
+ g_free (l->data);
+
+ processor->private->current_device =
+ processor->private->devices =
+ g_list_delete_link (processor->private->devices, l);
+ }
+ }
+
+ /* If we have no further devices to iterate */
+ if (!processor->private->current_device) {
+ process_devices_stop (processor);
+ process_next (processor);
+ return;
+ }
+
+ process_device (processor, processor->private->current_device->data);
+}
+
+static void
+process_modules_start (TrackerProcessor *processor)
+{
+ g_message ("Processor has started iterating %d modules",
+ g_list_length (processor->private->modules));
+
+ if (processor->private->timer) {
+ g_timer_destroy (processor->private->timer);
+ }
+
+ processor->private->timer = g_timer_new ();
+
+ processor->private->finished_modules = FALSE;
+
+ processor->private->directories_found = 0;
+ processor->private->directories_ignored = 0;
+ processor->private->files_found = 0;
+ processor->private->files_ignored = 0;
+
+ process_module_next (processor);
+}
+
+static void
+process_modules_stop (TrackerProcessor *processor)
+{
+ if (processor->private->finished_modules) {
+ return;
+ }
+
+ g_message ("--------------------------------------------------");
+ g_message ("Processor has %s iterating modules",
+ processor->private->interrupted ? "been stopped while" : "finished");
+
+ processor->private->finished_modules = TRUE;
+
+ if (processor->private->interrupted) {
+ TrackerCrawler *crawler;
+
+ crawler = g_hash_table_lookup (processor->private->crawlers,
+ processor->private->current_module->data);
+ if (crawler) {
+ tracker_crawler_stop (crawler);
+ }
+
+ if (processor->private->timer) {
+ g_timer_destroy (processor->private->timer);
+ processor->private->timer = NULL;
+ }
+ } else {
+ gdouble elapsed;
+
+ if (processor->private->timer) {
+ g_timer_stop (processor->private->timer);
+ elapsed = g_timer_elapsed (processor->private->timer, NULL);
+ } else {
+ elapsed = 0;
+ }
+
+ g_message ("Module time taken : %4.4f seconds",
+ elapsed);
+ g_message ("Module directories: %d (%d ignored)",
+ processor->private->directories_found,
+ processor->private->directories_ignored);
+ g_message ("Module files : %d (%d ignored)",
+ processor->private->files_found,
+ processor->private->files_ignored);
+ }
+
+ g_message ("--------------------------------------------------\n");
+}
+
+static void
+process_devices_start (TrackerProcessor *processor)
+{
+ g_message ("Processor has started iterating %d devices",
+ g_list_length (processor->private->devices));
+
+ if (processor->private->timer) {
+ g_timer_destroy (processor->private->timer);
+ }
+
+ processor->private->timer = g_timer_new ();
+
+ processor->private->finished_devices = FALSE;
+
+ processor->private->directories_found = 0;
+ processor->private->directories_ignored = 0;
+ processor->private->files_found = 0;
+ processor->private->files_ignored = 0;
+
+ process_device_next (processor);
+}
+
+static void
+process_devices_stop (TrackerProcessor *processor)
+{
+ if (processor->private->finished_devices) {
+ return;
+ }
+
+ g_message ("--------------------------------------------------");
+ g_message ("Processor has %s iterating devices",
+ processor->private->interrupted ? "been stopped while" : "finished");
+
+ processor->private->finished_devices = TRUE;
+
+ if (processor->private->interrupted) {
+ TrackerCrawler *crawler;
+
+ crawler = g_hash_table_lookup (processor->private->crawlers, "files");
+ if (crawler) {
+ tracker_crawler_stop (crawler);
+ }
+
+ if (processor->private->timer) {
+ g_timer_destroy (processor->private->timer);
+ processor->private->timer = NULL;
+ }
+ } else {
+ gdouble elapsed;
+
+ if (processor->private->timer) {
+ g_timer_stop (processor->private->timer);
+ elapsed = g_timer_elapsed (processor->private->timer, NULL);
+ } else {
+ elapsed = 0;
+ }
+
+ g_message ("Device time taken : %4.4f seconds",
+ elapsed);
+ g_message ("Device directories: %d (%d ignored)",
+ processor->private->directories_found,
+ processor->private->directories_ignored);
+ g_message ("Device files : %d (%d ignored)",
+ processor->private->files_found,
+ processor->private->files_ignored);
+ }
+
+ g_message ("--------------------------------------------------\n");
+}
+
+static void
+process_continue (TrackerProcessor *processor)
+{
+ if (!processor->private->finished_modules) {
+ process_module_next (processor);
+ return;
+ }
+
+ if (!processor->private->finished_devices) {
+ process_device_next (processor);
+ return;
+ }
+
+ /* Nothing to do */
+}
+
+static void
+process_next (TrackerProcessor *processor)
+{
+ if (!processor->private->finished_modules) {
+ process_modules_start (processor);
+ return;
+ }
+
+ if (!processor->private->finished_devices) {
+ process_devices_start (processor);
+ return;
+ }
+
+ /* Only do this the first time, otherwise the results are
+ * likely to be inaccurate. Devices can be added or removed so
+ * we can't assume stats are correct.
+ */
+ if (tracker_status_get_is_initial_check ()) {
+ g_message ("--------------------------------------------------");
+ g_message ("Total directories : %d (%d ignored)",
+ processor->private->total_directories_found,
+ processor->private->total_directories_ignored);
+ g_message ("Total files : %d (%d ignored)",
+ processor->private->total_files_found,
+ processor->private->total_files_ignored);
+ g_message ("Total monitors : %d",
+ tracker_monitor_get_count (processor->private->monitor, NULL));
+ g_message ("--------------------------------------------------\n");
+ }
+
+ /* Now we have finished crawling, we enable monitor events */
+ g_message ("Enabling monitor events");
+ tracker_monitor_set_enabled (processor->private->monitor, TRUE);
+
+ /* Now we set the state to IDLE, the reason we do this, is it
+ * allows us to either return to an idle state if there was
+ * nothing to do, OR it allows us to start handling the
+ * queues of files we just crawled. The queue handler won't
+ * send files to the indexer while we are PENDING or WATCHING.
+ */
+ tracker_status_set_and_signal (TRACKER_STATUS_IDLE);
+}
+
+static void
+process_finish (TrackerProcessor *processor)
+{
+ /* TODO: Optimize DBs
+ tracker_status_set_and_signal (TRACKER_STATUS_OPTIMIZING);
+ tracker_db_manager_optimize ();
+ */
+
+ /* All done */
+ tracker_status_set_and_signal (TRACKER_STATUS_IDLE);
+
+ /* Set our internal state */
+ tracker_status_set_is_initial_check (FALSE);
+
+ g_signal_emit (processor, signals[FINISHED], 0);
+}
+
+static void
+process_check_completely_finished (TrackerProcessor *processor)
+{
+ if (!processor->private->finished_sending ||
+ !processor->private->finished_indexer) {
+ return;
+ }
+
+ process_finish (processor);
+}
+
+#if 0
+static void
+indexer_started_cb (TrackerIndexer *indexer,
+ gpointer user_data)
+{
+ TrackerProcessor *processor;
+
+ processor = user_data;
+
+ processor->private->finished_indexer = FALSE;
+}
+
+static void
+indexer_finished_cb (TrackerIndexer *indexer,
+ gdouble seconds_elapsed,
+ guint items_processed,
+ guint items_indexed,
+ gboolean interrupted,
+ gpointer user_data)
+{
+ TrackerProcessor *processor;
+
+ processor = user_data;
+
+ /* Save indexer's state */
+ processor->private->finished_indexer = TRUE;
+ process_check_completely_finished (processor);
+}
+#endif
+
+static void
+processor_files_check (TrackerProcessor *processor,
+ const gchar *module_name,
+ GFile *file,
+ gboolean is_directory)
+{
+ TrackerCrawler *crawler;
+ GQueue *queue;
+ gboolean ignored;
+ gchar *path;
+
+ path = g_file_get_path (file);
+ crawler = g_hash_table_lookup (processor->private->crawlers, module_name);
+ ignored = tracker_crawler_is_path_ignored (crawler, path, is_directory);
+
+ g_debug ("%s:'%s' (%s) (create monitor event or user request)",
+ ignored ? "Ignored" : "Found ",
+ path,
+ is_directory ? "DIR" : "FILE");
+
+ if (!ignored) {
+ if (is_directory) {
+ tracker_crawler_add_unexpected_path (crawler, path);
+ }
+
+ queue = g_hash_table_lookup (processor->private->items_created_queues, module_name);
+ g_queue_push_tail (queue, g_object_ref (file));
+
+ item_queue_handlers_set_up (processor);
+ }
+
+ g_free (path);
+}
+
+static void
+processor_files_update (TrackerProcessor *processor,
+ const gchar *module_name,
+ GFile *file,
+ gboolean is_directory)
+{
+ TrackerCrawler *crawler;
+ GQueue *queue;
+ gchar *path;
+ gboolean ignored;
+
+ path = g_file_get_path (file);
+ crawler = g_hash_table_lookup (processor->private->crawlers, module_name);
+ ignored = tracker_crawler_is_path_ignored (crawler, path, is_directory);
+
+ g_debug ("%s:'%s' (%s) (update monitor event or user request)",
+ ignored ? "Ignored" : "Found ",
+ path,
+ is_directory ? "DIR" : "FILE");
+
+ if (!ignored) {
+ queue = g_hash_table_lookup (processor->private->items_updated_queues, module_name);
+ g_queue_push_tail (queue, g_object_ref (file));
+
+ item_queue_handlers_set_up (processor);
+ }
+
+ g_free (path);
+}
+
+static void
+processor_files_delete (TrackerProcessor *processor,
+ const gchar *module_name,
+ GFile *file,
+ gboolean is_directory)
+{
+ TrackerCrawler *crawler;
+ GQueue *queue;
+ gchar *path;
+ gboolean ignored;
+
+ path = g_file_get_path (file);
+ crawler = g_hash_table_lookup (processor->private->crawlers, module_name);
+ ignored = tracker_crawler_is_path_ignored (crawler, path, is_directory);
+
+ g_debug ("%s:'%s' (%s) (delete monitor event or user request)",
+ ignored ? "Ignored" : "Found ",
+ path,
+ is_directory ? "DIR" : "FILE");
+
+ if (!ignored) {
+ queue = g_hash_table_lookup (processor->private->items_deleted_queues, module_name);
+ g_queue_push_tail (queue, g_object_ref (file));
+
+ item_queue_handlers_set_up (processor);
+ }
+
+ g_free (path);
+}
+
+static void
+processor_files_move (TrackerProcessor *processor,
+ const gchar *module_name,
+ GFile *file,
+ GFile *other_file,
+ gboolean is_directory)
+{
+ TrackerCrawler *crawler;
+ GQueue *queue;
+ gchar *path;
+ gchar *other_path;
+ gboolean path_ignored;
+ gboolean other_path_ignored;
+
+ path = g_file_get_path (file);
+ other_path = g_file_get_path (other_file);
+ crawler = g_hash_table_lookup (processor->private->crawlers, module_name);
+
+ path_ignored = tracker_crawler_is_path_ignored (crawler, path, is_directory);
+ other_path_ignored = tracker_crawler_is_path_ignored (crawler, other_path, is_directory);
+
+ g_debug ("%s:'%s'->'%s':%s (%s) (move monitor event or user request)",
+ path_ignored ? "Ignored" : "Found ",
+ path,
+ other_path,
+ other_path_ignored ? "Ignored" : " Found",
+ is_directory ? "DIR" : "FILE");
+
+ if (path_ignored && other_path_ignored) {
+ /* Do nothing */
+ } else if (path_ignored) {
+ /* Check new file */
+ if (!is_directory) {
+ queue = g_hash_table_lookup (processor->private->items_created_queues, module_name);
+ g_queue_push_tail (queue, g_object_ref (other_file));
+
+ item_queue_handlers_set_up (processor);
+ }
+
+ /* If this is a directory we need to crawl it */
+ tracker_crawler_add_unexpected_path (crawler, other_path);
+ } else if (other_path_ignored) {
+ /* Delete old file */
+ queue = g_hash_table_lookup (processor->private->items_deleted_queues, module_name);
+ g_queue_push_tail (queue, g_object_ref (file));
+
+ item_queue_handlers_set_up (processor);
+ } else {
+ /* Move old file to new file */
+ queue = g_hash_table_lookup (processor->private->items_moved_queues, module_name);
+ g_queue_push_tail (queue, g_object_ref (file));
+ g_queue_push_tail (queue, g_object_ref (other_file));
+
+ item_queue_handlers_set_up (processor);
+ }
+
+ g_free (other_path);
+ g_free (path);
+}
+
+static void
+monitor_item_created_cb (TrackerMonitor *monitor,
+ const gchar *module_name,
+ GFile *file,
+ gboolean is_directory,
+ gpointer user_data)
+{
+ processor_files_check (user_data, module_name, file, is_directory);
+}
+
+static void
+monitor_item_updated_cb (TrackerMonitor *monitor,
+ const gchar *module_name,
+ GFile *file,
+ gboolean is_directory,
+ gpointer user_data)
+{
+ processor_files_update (user_data, module_name, file, is_directory);
+}
+
+static void
+monitor_item_deleted_cb (TrackerMonitor *monitor,
+ const gchar *module_name,
+ GFile *file,
+ gboolean is_directory,
+ gpointer user_data)
+{
+ processor_files_delete (user_data, module_name, file, is_directory);
+}
+
+static void
+monitor_item_moved_cb (TrackerMonitor *monitor,
+ const gchar *module_name,
+ GFile *file,
+ GFile *other_file,
+ gboolean is_directory,
+ gboolean is_source_monitored,
+ gpointer user_data)
+{
+ if (!is_source_monitored) {
+ TrackerProcessor *processor;
+ TrackerCrawler *crawler;
+ gchar *path;
+
+ processor = user_data;
+
+ /* If the source is not monitored, we need to crawl it. */
+ path = g_file_get_path (other_file);
+ crawler = g_hash_table_lookup (processor->private->crawlers, module_name);
+ tracker_crawler_add_unexpected_path (crawler, path);
+ g_free (path);
+ } else {
+ processor_files_move (user_data, module_name, file, other_file, is_directory);
+ }
+}
+
+static void
+crawler_processing_file_cb (TrackerCrawler *crawler,
+ const gchar *module_name,
+ GFile *file,
+ gpointer user_data)
+{
+ TrackerProcessor *processor;
+ GQueue *queue;
+
+ processor = user_data;
+
+ /* Add files in queue to our queues to send to the indexer */
+ queue = g_hash_table_lookup (processor->private->items_created_queues, module_name);
+ g_queue_push_tail (queue, g_object_ref (file));
+
+ item_queue_handlers_set_up (processor);
+}
+
+static void
+crawler_processing_directory_cb (TrackerCrawler *crawler,
+ const gchar *module_name,
+ GFile *file,
+ gpointer user_data)
+{
+ TrackerProcessor *processor;
+ GQueue *queue;
+ gboolean add_monitor;
+
+ processor = user_data;
+
+ /* FIXME: Get ignored directories from .cfg? We know that
+ * normally these would have monitors because these
+ * directories are those crawled based on the module config.
+ */
+ add_monitor = TRUE;
+
+ /* Should we add? */
+ if (add_monitor) {
+ tracker_monitor_add (processor->private->monitor, module_name, file);
+ }
+
+ /* Add files in queue to our queues to send to the indexer */
+ queue = g_hash_table_lookup (processor->private->items_created_queues, module_name);
+ g_queue_push_tail (queue, g_object_ref (file));
+
+ item_queue_handlers_set_up (processor);
+}
+
+static void
+crawler_finished_cb (TrackerCrawler *crawler,
+ const gchar *module_name,
+ guint directories_found,
+ guint directories_ignored,
+ guint files_found,
+ guint files_ignored,
+ gpointer user_data)
+{
+ TrackerProcessor *processor;
+
+ processor = user_data;
+
+ /* Update stats */
+ processor->private->directories_found += directories_found;
+ processor->private->directories_ignored += directories_ignored;
+ processor->private->files_found += files_found;
+ processor->private->files_ignored += files_ignored;
+
+ processor->private->total_directories_found += directories_found;
+ processor->private->total_directories_ignored += directories_ignored;
+ processor->private->total_files_found += files_found;
+ processor->private->total_files_ignored += files_ignored;
+
+ /* Proceed to next thing to process */
+ process_continue (processor);
+}
+
+#ifdef HAVE_HAL
+
+static gchar *
+normalize_mount_point (const gchar *mount_point)
+{
+ if (g_str_has_suffix (mount_point, G_DIR_SEPARATOR_S)) {
+ return g_strdup (mount_point);
+ } else {
+ return g_strconcat (mount_point, G_DIR_SEPARATOR_S, NULL);
+ }
+}
+
+static void
+mount_point_added_cb (TrackerStorage *hal,
+ const gchar *udi,
+ const gchar *mount_point,
+ gpointer user_data)
+{
+ TrackerProcessor *processor;
+ TrackerProcessorPrivate *priv;
+ TrackerStatus status;
+ gchar *mp;
+
+ processor = user_data;
+
+ priv = processor->private;
+
+ status = tracker_status_get ();
+ mp = normalize_mount_point (mount_point);
+
+ /* Add removable device to list of known devices to iterate */
+ if (!g_list_find_custom (priv->devices, mp, (GCompareFunc) g_strcmp0)) {
+ priv->devices = g_list_append (priv->devices, mp);
+ }
+
+ /* Reset finished devices flag */
+ processor->private->finished_devices = FALSE;
+
+ /* If we are idle/not doing anything, start up the processor
+ * again so we handle the new location.
+ */
+ if (status == TRACKER_STATUS_INDEXING ||
+ status == TRACKER_STATUS_OPTIMIZING ||
+ status == TRACKER_STATUS_IDLE) {
+ /* If we are indexing then we must have already
+ * crawled all locations so we need to start up the
+ * processor again for the removable media once more.
+ */
+ process_next (processor);
+ }
+}
+
+static void
+mount_point_removed_cb (TrackerStorage *hal,
+ const gchar *udi,
+ const gchar *mount_point,
+ gpointer user_data)
+{
+ TrackerProcessor *processor;
+ TrackerProcessorPrivate *priv;
+ GFile *file;
+ GList *l;
+ gchar *mp;
+
+ processor = user_data;
+
+ priv = processor->private;
+ mp = normalize_mount_point (mount_point);
+
+ /* Remove directory from list of iterated_removable_media, so
+ * we don't traverse it.
+ */
+ l = g_list_find_custom (priv->devices, mp, (GCompareFunc) g_strcmp0);
+
+ /* Make sure we don't remove the current device we are
+ * processing, this is because we do this same clean up later
+ * in process_device_next()
+ */
+ if (l && l != priv->current_device) {
+ g_free (l->data);
+ priv->devices = g_list_delete_link (priv->devices, l);
+ }
+
+ /* Remove the monitor, the volumes are updated somewhere else
+ * in main.
+ */
+ file = g_file_new_for_path (mount_point);
+ tracker_monitor_remove_recursively (priv->monitor, file);
+ g_object_unref (file);
+
+ g_free (mp);
+}
+
+#endif /* HAVE_HAL */
+
+TrackerProcessor *
+tracker_processor_new (TrackerConfig *config,
+ TrackerStorage *storage)
+{
+ TrackerProcessor *processor;
+ TrackerProcessorPrivate *priv;
+ TrackerCrawler *crawler;
+ GList *l;
+
+ g_return_val_if_fail (TRACKER_IS_CONFIG (config), NULL);
+
+#ifdef HAVE_HAL
+ g_return_val_if_fail (TRACKER_IS_STORAGE (storage), NULL);
+#endif /* HAVE_HAL */
+
+ tracker_status_set_and_signal (TRACKER_STATUS_INITIALIZING);
+
+ processor = g_object_new (TRACKER_TYPE_PROCESSOR, NULL);
+ priv = processor->private;
+
+ /* Set up config */
+ priv->config = g_object_ref (config);
+
+#ifdef HAVE_HAL
+ /* Set up hal */
+ priv->hal = g_object_ref (storage);
+
+ priv->devices = tracker_storage_get_removable_device_roots (priv->hal);
+
+ g_signal_connect (priv->hal, "mount-point-added",
+ G_CALLBACK (mount_point_added_cb),
+ processor);
+ g_signal_connect (priv->hal, "mount-point-removed",
+ G_CALLBACK (mount_point_removed_cb),
+ processor);
+#endif /* HAVE_HAL */
+
+ /* Set up the crawlers now we have config and hal */
+ for (l = priv->modules; l; l = l->next) {
+ crawler = tracker_crawler_new (priv->config, l->data);
+
+ g_signal_connect (crawler, "processing-file",
+ G_CALLBACK (crawler_processing_file_cb),
+ processor);
+ g_signal_connect (crawler, "processing-directory",
+ G_CALLBACK (crawler_processing_directory_cb),
+ processor);
+ g_signal_connect (crawler, "finished",
+ G_CALLBACK (crawler_finished_cb),
+ processor);
+
+ g_hash_table_insert (priv->crawlers,
+ g_strdup (l->data),
+ crawler);
+ }
+
+ /* Set up the monitor */
+ priv->monitor = tracker_monitor_new (config);
+
+ g_message ("Disabling monitor events until we have crawled the file system");
+ tracker_monitor_set_enabled (priv->monitor, FALSE);
+
+ g_signal_connect (priv->monitor, "item-created",
+ G_CALLBACK (monitor_item_created_cb),
+ processor);
+ g_signal_connect (priv->monitor, "item-updated",
+ G_CALLBACK (monitor_item_updated_cb),
+ processor);
+ g_signal_connect (priv->monitor, "item-deleted",
+ G_CALLBACK (monitor_item_deleted_cb),
+ processor);
+ g_signal_connect (priv->monitor, "item-moved",
+ G_CALLBACK (monitor_item_moved_cb),
+ processor);
+
+#if 0
+ /* Set up the indexer and signalling to know when we are
+ * finished.
+ */
+
+ g_signal_connect (priv->indexer, "started",
+ G_CALLBACK (indexer_started_cb),
+ processor);
+ g_signal_connect (priv->indexer, "finished",
+ G_CALLBACK (indexer_finished_cb),
+ processor);
+#endif
+
+ return processor;
+}
+
+void
+tracker_processor_start (TrackerProcessor *processor)
+{
+ g_return_if_fail (TRACKER_IS_PROCESSOR (processor));
+
+ processor->private->been_started = TRUE;
+
+ processor->private->interrupted = FALSE;
+
+ processor->private->finished_modules = FALSE;
+ processor->private->finished_devices = FALSE;
+ processor->private->finished_sending = FALSE;
+ processor->private->finished_indexer = FALSE;
+
+ process_next (processor);
+}
+
+void
+tracker_processor_stop (TrackerProcessor *processor)
+{
+ g_return_if_fail (TRACKER_IS_PROCESSOR (processor));
+
+ if (!processor->private->been_started) {
+ return;
+ }
+
+ processor->private->interrupted = TRUE;
+
+ process_modules_stop (processor);
+ process_devices_stop (processor);
+
+ /* Queues? */
+
+ process_finish (processor);
+}
+
diff --git a/src/libtracker-miner/tracker-processor.h b/src/libtracker-miner/tracker-processor.h
new file mode 100644
index 0000000..e367b0c
--- /dev/null
+++ b/src/libtracker-miner/tracker-processor.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Nokia
+ *
+ * This library 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 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#ifndef __TRACKERD_PROCESSOR_H__
+#define __TRACKERD_PROCESSOR_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include <libtracker-common/tracker-storage.h>
+
+#include "tracker-config.h"
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_PROCESSOR (tracker_processor_get_type())
+#define TRACKER_PROCESSOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), TRACKER_TYPE_PROCESSOR, TrackerProcessor))
+#define TRACKER_PROCESSOR_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), TRACKER_TYPE_PROCESSOR, TrackerProcessorClass))
+#define TRACKER_IS_PROCESSOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), TRACKER_TYPE_PROCESSOR))
+#define TRACKER_IS_PROCESSOR_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), TRACKER_TYPE_PROCESSOR))
+#define TRACKER_PROCESSOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TRACKER_TYPE_PROCESSOR, TrackerProcessorClass))
+
+typedef struct TrackerProcessor TrackerProcessor;
+typedef struct TrackerProcessorClass TrackerProcessorClass;
+typedef struct TrackerProcessorPrivate TrackerProcessorPrivate;
+
+struct TrackerProcessor {
+ GObject parent;
+ TrackerProcessorPrivate *private;
+};
+
+struct TrackerProcessorClass {
+ GObjectClass parent;
+
+ void (*finished) (TrackerProcessor *processor);
+};
+
+GType tracker_processor_get_type (void) G_GNUC_CONST;
+
+TrackerProcessor *tracker_processor_new (TrackerConfig *config,
+ TrackerStorage *storage);
+void tracker_processor_start (TrackerProcessor *processor);
+void tracker_processor_stop (TrackerProcessor *processor);
+
+G_END_DECLS
+
+#endif /* __TRACKERD_PROCESSOR_H__ */
diff --git a/src/libtracker-miner/tracker-status.c b/src/libtracker-miner/tracker-status.c
new file mode 100644
index 0000000..191b39d
--- /dev/null
+++ b/src/libtracker-miner/tracker-status.c
@@ -0,0 +1,1097 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library 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 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.h"
+
+#include <sys/statvfs.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "tracker-status.h"
+
+#define DISK_SPACE_CHECK_FREQUENCY 10
+
+#define THROTTLE_DEFAULT 0
+#define THROTTLE_DEFAULT_ON_BATTERY 5
+
+#define PROCESS_PRIORITY_FOR_BUSY 19
+
+typedef struct {
+ TrackerStatus status;
+ TrackerStatus status_before_paused;
+ gpointer type_class;
+
+ guint disk_space_check_id;
+
+ gint cpu_priority;
+
+ TrackerConfig *config;
+ TrackerPower *hal;
+
+
+ gboolean is_readonly;
+ gboolean is_ready;
+ gboolean is_running;
+ gboolean is_first_time_index;
+ gboolean is_initial_check;
+ gboolean is_paused_manually;
+ gboolean is_paused_for_batt;
+ gboolean is_paused_for_io;
+ gboolean is_paused_for_space;
+ gboolean is_paused_for_dbus;
+ gboolean is_paused_for_unknown;
+ gboolean in_merge;
+} TrackerStatusPrivate;
+
+static void low_disk_space_limit_cb (GObject *gobject,
+ GParamSpec *arg1,
+ gpointer user_data);
+static void on_battery_cb (GObject *gobject,
+ GParamSpec *arg1,
+ gpointer user_data);
+static void on_low_battery_cb (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data);
+static void disk_space_check_stop (void);
+
+static GStaticPrivate private_key = G_STATIC_PRIVATE_INIT;
+
+static void
+private_free (gpointer data)
+{
+ TrackerStatusPrivate *private;
+
+ private = data;
+
+ if (private->disk_space_check_id) {
+ g_source_remove (private->disk_space_check_id);
+ }
+
+ g_signal_handlers_disconnect_by_func (private->config,
+ low_disk_space_limit_cb,
+ NULL);
+
+ g_object_unref (private->config);
+
+#ifdef HAVE_HAL
+ g_signal_handlers_disconnect_by_func (private->hal,
+ on_battery_cb,
+ NULL);
+ g_signal_handlers_disconnect_by_func (private->hal,
+ on_low_battery_cb,
+ NULL);
+
+ g_object_unref (private->hal);
+#endif
+
+ if (private->type_class) {
+ g_type_class_unref (private->type_class);
+ }
+
+ g_free (private);
+}
+
+/*
+ * Handle Indexer pausing/continuation
+ */
+
+static void
+indexer_recheck (gboolean should_block,
+ gboolean should_signal_small_changes)
+{
+ TrackerStatusPrivate *private;
+
+ private = g_static_private_get (&private_key);
+ g_return_if_fail (private != NULL);
+
+ /* Are we paused in any way? */
+ if (private->is_paused_manually ||
+ private->is_paused_for_batt ||
+ private->is_paused_for_io ||
+ private->is_paused_for_space ||
+ private->is_paused_for_dbus ||
+ private->is_paused_for_unknown) {
+ /* We are paused, but our status is NOT paused? */
+ if (private->status != TRACKER_STATUS_PAUSED) {
+ tracker_status_set_and_signal (TRACKER_STATUS_PAUSED);
+
+ return;
+ }
+ } else {
+ /* We are not paused, but our status is paused */
+ if (private->status == TRACKER_STATUS_PAUSED) {
+ tracker_status_set_and_signal (private->status_before_paused);
+
+ return;
+ }
+ }
+
+ /* Simply signal because in this case, state hasn't changed
+ * but one of the reasons for being paused has changed.
+ */
+ if (should_signal_small_changes) {
+ tracker_status_signal ();
+ }
+}
+
+static gboolean
+disk_space_check (void)
+{
+ TrackerStatusPrivate *private;
+ struct statvfs st;
+ gint limit;
+ gchar *data_dir;
+
+ private = g_static_private_get (&private_key);
+ g_return_val_if_fail (private != NULL, FALSE);
+
+ limit = tracker_config_get_low_disk_space_limit (private->config);
+
+ if (limit < 1) {
+ return FALSE;
+ }
+
+ data_dir = g_build_filename (g_get_user_cache_dir (),
+ "tracker",
+ NULL);
+
+ if (statvfs (data_dir, &st) == -1) {
+ g_warning ("Could not statvfs() '%s'", data_dir);
+ g_free (data_dir);
+ return FALSE;
+ }
+
+ g_free (data_dir);
+
+ if (((long long) st.f_bavail * 100 / st.f_blocks) <= limit) {
+ g_message ("WARNING: Available disk space is below configured "
+ "threshold for acceptable working (%d%%)",
+ limit);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+disk_space_check_cb (gpointer user_data)
+{
+ if (disk_space_check ()) {
+ tracker_status_set_is_paused_for_space (TRUE);
+ } else {
+ tracker_status_set_is_paused_for_space (FALSE);
+ }
+
+ return TRUE;
+}
+
+static void
+disk_space_check_start (void)
+{
+ TrackerStatusPrivate *private;
+ gint limit;
+
+ private = g_static_private_get (&private_key);
+ g_return_if_fail (private != NULL);
+
+ if (private->disk_space_check_id != 0) {
+ return;
+ }
+
+ limit = tracker_config_get_low_disk_space_limit (private->config);
+
+ if (limit != -1) {
+ g_message ("Starting disk space check for every %d seconds",
+ DISK_SPACE_CHECK_FREQUENCY);
+ private->disk_space_check_id =
+ g_timeout_add_seconds (DISK_SPACE_CHECK_FREQUENCY,
+ disk_space_check_cb,
+ NULL);
+
+ /* Call the function now too to make sure we have an
+ * initial value too!
+ */
+ disk_space_check_cb (NULL);
+ } else {
+ g_message ("Not setting disk space, configuration is set to -1 (disabled)");
+ }
+}
+
+static void
+disk_space_check_stop (void)
+{
+ TrackerStatusPrivate *private;
+
+ private = g_static_private_get (&private_key);
+ g_return_if_fail (private != NULL);
+
+ if (private->disk_space_check_id) {
+ g_message ("Stopping disk space check");
+ g_source_remove (private->disk_space_check_id);
+ private->disk_space_check_id = 0;
+ }
+}
+
+static void
+low_disk_space_limit_cb (GObject *gobject,
+ GParamSpec *arg1,
+ gpointer user_data)
+{
+ disk_space_check_cb (NULL);
+}
+
+#ifdef HAVE_HAL
+
+static void
+set_up_throttle (gboolean debugging)
+{
+ TrackerStatusPrivate *private;
+ gint throttle;
+
+ private = g_static_private_get (&private_key);
+ g_return_if_fail (private != NULL);
+
+ /* If on a laptop battery and the throttling is default (i.e.
+ * 0), then set the throttle to be higher so we don't kill
+ * the laptop battery.
+ */
+ throttle = tracker_config_get_throttle (private->config);
+
+ if (tracker_power_get_on_battery (private->hal)) {
+ if (debugging) {
+ g_message ("We are running on battery");
+ }
+
+ if (throttle == THROTTLE_DEFAULT) {
+ tracker_config_set_throttle (private->config,
+ THROTTLE_DEFAULT_ON_BATTERY);
+
+ if (debugging) {
+ g_message ("Setting throttle from %d to %d",
+ throttle,
+ THROTTLE_DEFAULT_ON_BATTERY);
+ }
+ } else {
+ if (debugging) {
+ g_message ("Not setting throttle, it is currently set to %d",
+ throttle);
+ }
+ }
+ } else {
+ if (debugging) {
+ g_message ("We are not running on battery");
+ }
+
+ if (throttle == THROTTLE_DEFAULT_ON_BATTERY) {
+ tracker_config_set_throttle (private->config,
+ THROTTLE_DEFAULT);
+
+ if (debugging) {
+ g_message ("Setting throttle from %d to %d",
+ throttle,
+ THROTTLE_DEFAULT);
+ }
+ } else {
+ if (debugging) {
+ g_message ("Not setting throttle, it is currently set to %d",
+ throttle);
+ }
+ }
+ }
+}
+
+static void
+on_battery_cb (GObject *gobject,
+ GParamSpec *arg1,
+ gpointer user_data)
+{
+ set_up_throttle (TRUE);
+}
+
+static void
+on_low_battery_cb (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ TrackerStatusPrivate *private;
+ gboolean on_low_battery;
+ gboolean on_battery;
+
+ private = g_static_private_get (&private_key);
+ g_return_if_fail (private != NULL);
+
+ on_low_battery = tracker_power_get_on_low_battery (private->hal);
+ on_battery = tracker_power_get_on_battery (private->hal);
+
+ /* FIXME: This could be a configuration option */
+ if (on_battery) {
+ if (on_low_battery) {
+ /* Running on low batteries, stop indexing for now */
+ tracker_status_set_is_paused_for_batt (TRUE);
+ } else {
+ tracker_status_set_is_paused_for_batt (FALSE);
+ }
+ } else {
+ tracker_status_set_is_paused_for_batt (FALSE);
+ }
+
+ set_up_throttle (FALSE);
+}
+
+#endif /* HAVE_HAL */
+
+gboolean
+tracker_status_init (TrackerConfig *config,
+ TrackerPower *hal)
+{
+ GType type;
+ TrackerStatusPrivate *private;
+
+ g_return_val_if_fail (TRACKER_IS_CONFIG (config), FALSE);
+
+ private = g_static_private_get (&private_key);
+ if (private) {
+ g_warning ("Already initialized (%s)",
+ __FUNCTION__);
+ return FALSE;
+ }
+
+ private = g_new0 (TrackerStatusPrivate, 1);
+
+ private->status = TRACKER_STATUS_INITIALIZING;
+ private->status_before_paused = private->status;
+
+ /* Since we don't reference this enum anywhere, we do
+ * it here to make sure it exists when we call
+ * g_type_class_peek(). This wouldn't be necessary if
+ * it was a param in a GObject for example.
+ *
+ * This does mean that we are leaking by 1 reference
+ * here and should clean it up, but it doesn't grow so
+ * this is acceptable.
+ */
+ type = tracker_status_get_type ();
+ private->type_class = g_type_class_ref (type);
+
+ private->config = g_object_ref (config);
+
+ g_signal_connect (private->config, "notify::low-disk-space-limit",
+ G_CALLBACK (low_disk_space_limit_cb),
+ NULL);
+
+
+#ifdef HAVE_HAL
+ private->hal = g_object_ref (hal);
+
+ g_message ("Setting battery percentage checking");
+ g_signal_connect (private->hal, "notify::on-low-battery",
+ G_CALLBACK (on_low_battery_cb),
+ NULL);
+ g_signal_connect (private->hal, "notify::on-battery",
+ G_CALLBACK (on_battery_cb),
+ NULL);
+#endif
+
+ private->is_readonly = FALSE;
+ private->is_ready = FALSE;
+ private->is_running = FALSE;
+ private->is_first_time_index = FALSE;
+ private->is_initial_check = FALSE;
+ private->is_paused_manually = FALSE;
+ private->is_paused_for_batt = FALSE;
+ private->is_paused_for_io = FALSE;
+ private->is_paused_for_space = FALSE;
+ private->is_paused_for_dbus = FALSE;
+ private->is_paused_for_unknown = FALSE;
+ private->in_merge = FALSE;
+
+ /* Get process priority on start up:
+ * We use nice() when crawling so we don't steal all
+ * the processor time, for all other states, we return the
+ * nice() value to what it was.
+ *
+ * NOTE: We set errno first because -1 is a valid priority and
+ * we need to check it isn't an error.
+ */
+ errno = 0;
+ private->cpu_priority = getpriority (PRIO_PROCESS, 0);
+
+ if ((private->cpu_priority < 0) && errno) {
+ const gchar *str = g_strerror (errno);
+
+ g_message ("Couldn't get nice value, %s",
+ str ? str : "no error given");
+
+ /* Default to 0 */
+ private->cpu_priority = 0;
+ }
+
+ g_message ("Current process priority is set to %d, this will be used for all non-crawling states",
+ private->cpu_priority);
+
+ g_static_private_set (&private_key,
+ private,
+ private_free);
+
+ /* Do initial disk space check, we don't start the timeout
+ * which checks the disk space every 10 seconds here because
+ * we might not have enough to begin with. So we do one
+ * initial check to set the state correctly.
+ */
+ disk_space_check_cb (NULL);
+
+ return TRUE;
+}
+
+void
+tracker_status_shutdown (void)
+{
+ TrackerStatusPrivate *private;
+
+ private = g_static_private_get (&private_key);
+ if (!private) {
+ g_warning ("Not initialized (%s)",
+ __FUNCTION__);
+ return;
+ }
+
+ g_static_private_free (&private_key);
+}
+
+gboolean
+tracker_status_is_initialized (void)
+{
+ TrackerStatusPrivate *private;
+
+ private = g_static_private_get (&private_key);
+
+ return (private != NULL);
+}
+
+GType
+tracker_status_get_type (void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ static const GEnumValue values[] = {
+ { TRACKER_STATUS_INITIALIZING,
+ "TRACKER_STATUS_INITIALIZING",
+ "Initializing" },
+ { TRACKER_STATUS_WATCHING,
+ "TRACKER_STATUS_WATCHING",
+ "Watching" },
+ { TRACKER_STATUS_INDEXING,
+ "TRACKER_STATUS_INDEXING",
+ "Indexing" },
+ { TRACKER_STATUS_PAUSED,
+ "TRACKER_STATUS_PAUSED",
+ "Paused" },
+ { TRACKER_STATUS_PENDING,
+ "TRACKER_STATUS_PENDING",
+ "Pending" },
+ { TRACKER_STATUS_OPTIMIZING,
+ "TRACKER_STATUS_OPTIMIZING",
+ "Optimizing" },
+ { TRACKER_STATUS_IDLE,
+ "TRACKER_STATUS_IDLE",
+ "Idle" },
+ { TRACKER_STATUS_SHUTDOWN,
+ "TRACKER_STATUS_SHUTDOWN",
+ "Shutdown" },
+ { 0, NULL, NULL }
+ };
+
+ type = g_enum_register_static ("TrackerStatus", values);
+ }
+
+ return type;
+}
+
+const gchar *
+tracker_status_to_string (TrackerStatus status)
+{
+ GType type;
+ GEnumClass *enum_class;
+ GEnumValue *enum_value;
+
+ type = tracker_status_get_type ();
+ enum_class = G_ENUM_CLASS (g_type_class_peek (type));
+ enum_value = g_enum_get_value (enum_class, status);
+
+ if (!enum_value) {
+ enum_value = g_enum_get_value (enum_class, TRACKER_STATUS_IDLE);
+ }
+
+ return enum_value->value_nick;
+}
+
+TrackerStatus
+tracker_status_get (void)
+{
+ TrackerStatusPrivate *private;
+
+ private = g_static_private_get (&private_key);
+ g_return_val_if_fail (private != NULL, TRACKER_STATUS_INITIALIZING);
+
+ return private->status;
+}
+
+const gchar *
+tracker_status_get_as_string (void)
+{
+ TrackerStatusPrivate *private;
+
+ private = g_static_private_get (&private_key);
+ g_return_val_if_fail (private != NULL, tracker_status_to_string (TRACKER_STATUS_INITIALIZING));
+
+ return tracker_status_to_string (private->status);
+}
+
+static void
+status_set_priority (TrackerStatus old_status,
+ TrackerStatus new_status,
+ gint special_priority,
+ gint default_priority)
+{
+ /* Note, we can't use -1 here, so we use a value
+ * outside the nice values from -20->19
+ */
+ gint new_priority = 100;
+
+ /* Handle priority changes */
+ if (new_status == TRACKER_STATUS_PENDING) {
+ new_priority = special_priority;
+ } else if (old_status == TRACKER_STATUS_PENDING) {
+ new_priority = default_priority;
+ }
+
+ if (new_priority != 100) {
+ g_message ("Setting process priority to %d (%s)",
+ new_priority,
+ new_priority == default_priority ? "default" : "special");
+
+ if (nice (new_priority) == -1) {
+ const gchar *str = g_strerror (errno);
+
+ g_message ("Couldn't set nice value to %d, %s",
+ new_priority,
+ str ? str : "no error given");
+ }
+ }
+}
+
+void
+tracker_status_set (TrackerStatus new_status)
+{
+ TrackerStatusPrivate *private;
+ gboolean should_be_paused;
+ gboolean invalid_new_state;
+
+ private = g_static_private_get (&private_key);
+ g_return_if_fail (private != NULL);
+
+
+ should_be_paused =
+ private->is_paused_manually ||
+ private->is_paused_for_batt ||
+ private->is_paused_for_io ||
+ private->is_paused_for_space ||
+ private->is_paused_for_dbus ||
+ private->is_paused_for_unknown;
+
+ invalid_new_state =
+ should_be_paused &&
+ new_status != TRACKER_STATUS_PAUSED;
+
+ g_message ("State change from '%s' --> '%s' %s",
+ tracker_status_to_string (private->status),
+ tracker_status_to_string (new_status),
+ invalid_new_state ? "attempted with pause conditions, doing nothing" : "");
+
+ /* Don't set previous status to the same as we are now,
+ * otherwise we could end up setting PAUSED and our old state
+ * to return to is also PAUSED.
+ *
+ * We really should start using a proper state machine here,
+ * but I am avoiding it for now, -mr.
+ */
+ if (private->status_before_paused != new_status &&
+ private->status != TRACKER_STATUS_PAUSED) {
+ /* ALWAYS only set this after checking against the
+ * previous value
+ */
+ private->status_before_paused = private->status;
+ }
+
+ /* State machine */
+ if (private->status != new_status) {
+ /* The reason we have this check, is that it is
+ * possible that we are trying to set our state to
+ * IDLE after finishing crawling but actually, we
+ * should be in a PAUSED state due to another flag,
+ * such as low disk space. So we force PAUSED state.
+ * However, the interesting thing here is, we must
+ * remember to set the OLD state so we go back to the
+ * state as set by the caller. If we don't we end up
+ * going back to PENDING/WATCHING instead of IDLE when
+ * we come out of being PAUSED.
+ *
+ * FIXME: Should we ONLY cater for IDLE here? -mr
+ */
+
+ if (invalid_new_state) {
+ g_message ("Attempt to set state to '%s' with pause conditions, doing nothing",
+ tracker_status_to_string (new_status));
+
+ if (private->status != TRACKER_STATUS_PAUSED) {
+ tracker_status_set (TRACKER_STATUS_PAUSED);
+ }
+
+ /* Set last state so we know what to return
+ * to when we come out of PAUSED
+ */
+
+ private->status_before_paused = new_status;
+
+ return;
+ }
+
+ /* Make sure we are using the correct priority for
+ * the new state.
+ */
+ status_set_priority (new_status,
+ private->status,
+ TRACKER_STATUS_PENDING,
+ private->cpu_priority);
+
+ /* Handle disk space moitor changes
+ *
+ * So these are the conditions when we need checks
+ * running:
+ *
+ * 1. Are we low on space?
+ * 3. State is INDEXING or OPTIMIZING (use of disk)
+ */
+ if (private->is_paused_for_space ||
+ new_status == TRACKER_STATUS_INDEXING ||
+ new_status == TRACKER_STATUS_OPTIMIZING) {
+ disk_space_check_start ();
+ } else {
+ disk_space_check_stop ();
+ }
+ }
+
+ private->status = new_status;
+}
+
+void
+tracker_status_signal (void)
+{
+#if 0
+ TrackerStatusPrivate *private;
+ GObject *object;
+ gboolean pause_for_io;
+
+ private = g_static_private_get (&private_key);
+ g_return_if_fail (private != NULL);
+
+ object = tracker_dbus_get_object (TRACKER_TYPE_DAEMON);
+
+ /* There are times on startup whe we haven't initialized the
+ * DBus objects yet so signalling status is not practical.
+ */
+ if (!object) {
+ return;
+ }
+
+ /* NOTE: We have to combine pausing for IO, pausing for
+ * space and pausing for unknown reasons here because we can't
+ * change API yet.
+ */
+ pause_for_io =
+ private->is_paused_for_io ||
+ private->is_paused_for_space ||
+ private->is_paused_for_dbus ||
+ private->is_paused_for_unknown;
+
+ g_signal_emit_by_name (object,
+ "index-state-change",
+ tracker_status_to_string (private->status),
+ private->is_first_time_index,
+ private->in_merge,
+ private->is_paused_manually,
+ private->is_paused_for_batt,
+ pause_for_io,
+ !private->is_readonly);
+#endif
+}
+
+void
+tracker_status_set_and_signal (TrackerStatus new_status)
+{
+ TrackerStatusPrivate *private;
+ gboolean emit;
+
+ private = g_static_private_get (&private_key);
+ g_return_if_fail (private != NULL);
+
+ emit = private->status != new_status;
+
+ if (!emit) {
+ return;
+ }
+
+ tracker_status_set (new_status);
+ tracker_status_signal ();
+}
+
+gboolean
+tracker_status_get_is_readonly (void)
+{
+ TrackerStatusPrivate *private;
+
+ private = g_static_private_get (&private_key);
+ g_return_val_if_fail (private != NULL, FALSE);
+
+ return private->is_readonly;
+}
+
+void
+tracker_status_set_is_readonly (gboolean value)
+{
+ TrackerStatusPrivate *private;
+ gboolean emit;
+
+ private = g_static_private_get (&private_key);
+ g_return_if_fail (private != NULL);
+
+ emit = private->is_readonly != value;
+
+ if (!emit) {
+ return;
+ }
+
+ /* Set value */
+ private->is_readonly = value;
+
+ /* Signal the status change */
+ tracker_status_signal ();
+}
+
+gboolean
+tracker_status_get_is_ready (void)
+{
+ TrackerStatusPrivate *private;
+
+ private = g_static_private_get (&private_key);
+ g_return_val_if_fail (private != NULL, FALSE);
+
+ return private->is_ready;
+}
+
+void
+tracker_status_set_is_ready (gboolean value)
+{
+ TrackerStatusPrivate *private;
+ gboolean emit;
+
+ private = g_static_private_get (&private_key);
+ g_return_if_fail (private != NULL);
+
+ emit = private->is_ready != value;
+
+ if (!emit) {
+ return;
+ }
+
+ /* Set value */
+ private->is_ready = value;
+
+ /* Signal the status change */
+ tracker_status_signal ();
+}
+
+gboolean
+tracker_status_get_is_running (void)
+{
+ TrackerStatusPrivate *private;
+
+ private = g_static_private_get (&private_key);
+ g_return_val_if_fail (private != NULL, FALSE);
+
+ return private->is_running;
+}
+
+void
+tracker_status_set_is_running (gboolean value)
+{
+ TrackerStatusPrivate *private;
+ gboolean emit;
+
+ private = g_static_private_get (&private_key);
+ g_return_if_fail (private != NULL);
+
+ emit = private->is_running != value;
+
+ if (!emit) {
+ return;
+ }
+
+ /* Set value */
+ private->is_running = value;
+
+ /* Signal the status change */
+ tracker_status_signal ();
+}
+
+gboolean
+tracker_status_get_is_first_time_index (void)
+{
+ TrackerStatusPrivate *private;
+
+ private = g_static_private_get (&private_key);
+ g_return_val_if_fail (private != NULL, FALSE);
+
+ return private->is_first_time_index;
+}
+
+void
+tracker_status_set_is_first_time_index (gboolean value)
+{
+ TrackerStatusPrivate *private;
+ gboolean emit;
+
+ private = g_static_private_get (&private_key);
+ g_return_if_fail (private != NULL);
+
+ emit = private->is_first_time_index != value;
+
+ if (!emit) {
+ return;
+ }
+
+ /* Set value */
+ private->is_first_time_index = value;
+
+ /* Signal the status change */
+ tracker_status_signal ();
+}
+
+gboolean
+tracker_status_get_is_initial_check (void)
+{
+ TrackerStatusPrivate *private;
+
+ private = g_static_private_get (&private_key);
+ g_return_val_if_fail (private != NULL, FALSE);
+
+ return private->is_initial_check;
+}
+
+void
+tracker_status_set_is_initial_check (gboolean value)
+{
+ TrackerStatusPrivate *private;
+
+ private = g_static_private_get (&private_key);
+ g_return_if_fail (private != NULL);
+
+ /* Set value */
+ private->is_initial_check = value;
+
+ /* We don't need to signal this */
+}
+
+gboolean
+tracker_status_get_in_merge (void)
+{
+ TrackerStatusPrivate *private;
+
+ private = g_static_private_get (&private_key);
+ g_return_val_if_fail (private != NULL, FALSE);
+
+ return private->in_merge;
+}
+
+void
+tracker_status_set_in_merge (gboolean value)
+{
+ TrackerStatusPrivate *private;
+ gboolean emit;
+
+ private = g_static_private_get (&private_key);
+ g_return_if_fail (private != NULL);
+
+ emit = private->in_merge != value;
+
+ if (!emit) {
+ return;
+ }
+
+ /* Set value */
+ private->in_merge = value;
+
+ /* Signal the status change */
+ tracker_status_signal ();
+}
+
+gboolean
+tracker_status_get_is_paused_manually (void)
+{
+ TrackerStatusPrivate *private;
+
+ private = g_static_private_get (&private_key);
+ g_return_val_if_fail (private != NULL, FALSE);
+
+ return private->is_paused_manually;
+}
+
+void
+tracker_status_set_is_paused_manually (gboolean value)
+{
+ TrackerStatusPrivate *private;
+ gboolean emit;
+
+ private = g_static_private_get (&private_key);
+ g_return_if_fail (private != NULL);
+
+ /* Set value */
+ emit = private->is_paused_manually != value;
+ private->is_paused_manually = value;
+
+ /* Set indexer state and our state to paused or not */
+ indexer_recheck (FALSE, emit);
+}
+
+gboolean
+tracker_status_get_is_paused_for_batt (void)
+{
+ TrackerStatusPrivate *private;
+
+ private = g_static_private_get (&private_key);
+ g_return_val_if_fail (private != NULL, FALSE);
+
+ return private->is_paused_for_batt;
+}
+
+void
+tracker_status_set_is_paused_for_batt (gboolean value)
+{
+ TrackerStatusPrivate *private;
+ gboolean emit;
+
+ private = g_static_private_get (&private_key);
+ g_return_if_fail (private != NULL);
+
+ /* Set value */
+ emit = private->is_paused_for_batt != value;
+ private->is_paused_for_batt = value;
+
+ /* Set indexer state and our state to paused or not */
+ indexer_recheck (FALSE, emit);
+}
+
+gboolean
+tracker_status_get_is_paused_for_io (void)
+{
+ TrackerStatusPrivate *private;
+
+ private = g_static_private_get (&private_key);
+ g_return_val_if_fail (private != NULL, FALSE);
+
+ return private->is_paused_for_io;
+}
+
+void
+tracker_status_set_is_paused_for_io (gboolean value)
+{
+ TrackerStatusPrivate *private;
+ gboolean emit;
+
+ private = g_static_private_get (&private_key);
+ g_return_if_fail (private != NULL);
+
+ /* Set value */
+ emit = private->is_paused_for_io != value;
+ private->is_paused_for_io = value;
+
+ /* Set indexer state and our state to paused or not */
+ indexer_recheck (FALSE, emit);
+}
+
+gboolean
+tracker_status_get_is_paused_for_space (void)
+{
+ TrackerStatusPrivate *private;
+
+ private = g_static_private_get (&private_key);
+ g_return_val_if_fail (private != NULL, FALSE);
+
+ return private->is_paused_for_space;
+}
+
+void
+tracker_status_set_is_paused_for_space (gboolean value)
+{
+ TrackerStatusPrivate *private;
+ gboolean emit;
+
+ private = g_static_private_get (&private_key);
+ g_return_if_fail (private != NULL);
+
+ /* Set value */
+ emit = private->is_paused_for_space != value;
+ private->is_paused_for_space = value;
+
+ /* Set indexer state and our state to paused or not */
+ indexer_recheck (FALSE, emit);
+}
+
+gboolean
+tracker_status_get_is_paused_for_dbus (void)
+{
+ TrackerStatusPrivate *private;
+
+ private = g_static_private_get (&private_key);
+ g_return_val_if_fail (private != NULL, FALSE);
+
+ return private->is_paused_for_space;
+}
+
+void
+tracker_status_set_is_paused_for_dbus (gboolean value)
+{
+ TrackerStatusPrivate *private;
+ gboolean emit;
+
+ private = g_static_private_get (&private_key);
+ g_return_if_fail (private != NULL);
+
+ /* Set value */
+ emit = private->is_paused_for_dbus != value;
+ private->is_paused_for_dbus = value;
+
+ /* Set indexer state and our state to paused or not */
+ indexer_recheck (TRUE, emit);
+}
diff --git a/src/libtracker-miner/tracker-status.h b/src/libtracker-miner/tracker-status.h
new file mode 100644
index 0000000..d73e13b
--- /dev/null
+++ b/src/libtracker-miner/tracker-status.h
@@ -0,0 +1,96 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library 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 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#ifndef __TRACKERD_STATUS_H__
+#define __TRACKERD_STATUS_H__
+
+#include <glib-object.h>
+
+#include <libtracker-common/tracker-power.h>
+
+#include "tracker-config.h"
+
+G_BEGIN_DECLS
+
+#define TRACKER_TYPE_STATUS (tracker_status_get_type ())
+
+typedef enum {
+ TRACKER_STATUS_INITIALIZING,
+ TRACKER_STATUS_WATCHING,
+ TRACKER_STATUS_INDEXING,
+ TRACKER_STATUS_PAUSED,
+ TRACKER_STATUS_PENDING,
+ TRACKER_STATUS_OPTIMIZING,
+ TRACKER_STATUS_IDLE,
+ TRACKER_STATUS_SHUTDOWN
+} TrackerStatus;
+
+
+gboolean tracker_status_init (TrackerConfig *config,
+ TrackerPower *hal);
+void tracker_status_shutdown (void);
+
+gboolean tracker_status_is_initialized (void);
+
+GType tracker_status_get_type (void) G_GNUC_CONST;
+const gchar * tracker_status_to_string (TrackerStatus status);
+TrackerStatus tracker_status_get (void);
+const gchar * tracker_status_get_as_string (void);
+void tracker_status_set (TrackerStatus new_status);
+void tracker_status_set_and_signal (TrackerStatus new_status);
+void tracker_status_signal (void);
+
+gboolean tracker_status_get_is_readonly (void);
+void tracker_status_set_is_readonly (gboolean value);
+
+gboolean tracker_status_get_is_ready (void);
+void tracker_status_set_is_ready (gboolean value);
+
+gboolean tracker_status_get_is_running (void);
+void tracker_status_set_is_running (gboolean value);
+
+void tracker_status_set_is_first_time_index (gboolean value);
+gboolean tracker_status_get_is_first_time_index (void);
+
+void tracker_status_set_is_initial_check (gboolean value);
+gboolean tracker_status_get_is_initial_check (void);
+
+gboolean tracker_status_get_in_merge (void);
+void tracker_status_set_in_merge (gboolean value);
+
+gboolean tracker_status_get_is_paused_manually (void);
+void tracker_status_set_is_paused_manually (gboolean value);
+
+gboolean tracker_status_get_is_paused_for_batt (void);
+void tracker_status_set_is_paused_for_batt (gboolean value);
+
+gboolean tracker_status_get_is_paused_for_io (void);
+void tracker_status_set_is_paused_for_io (gboolean value);
+
+gboolean tracker_status_get_is_paused_for_space (void);
+void tracker_status_set_is_paused_for_space (gboolean value);
+
+gboolean tracker_status_get_is_paused_for_dbus (void);
+void tracker_status_set_is_paused_for_dbus (gboolean value);
+
+G_END_DECLS
+
+#endif /* __TRACKERD_STATUS_H__ */
diff --git a/src/libtracker-miner/tracker-utils.c b/src/libtracker-miner/tracker-utils.c
new file mode 100644
index 0000000..69b3fd5
--- /dev/null
+++ b/src/libtracker-miner/tracker-utils.c
@@ -0,0 +1,43 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009, Nokia (urho konttori nokia com)
+ *
+ * This library 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 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.h"
+
+#include "tracker-utils.h"
+
+void
+tracker_throttle (TrackerConfig *config,
+ gint multiplier)
+{
+ gint throttle;
+
+ g_return_if_fail (TRACKER_IS_CONFIG (config));
+
+ /* Get the throttle, add 5 (minimum value) so we don't do
+ * nothing and then multiply it by the factor given
+ */
+ throttle = tracker_config_get_throttle (config);
+ /* throttle += 5; */
+ throttle *= multiplier;
+
+ if (throttle > 0) {
+ g_usleep (throttle);
+ }
+}
diff --git a/src/libtracker-miner/tracker-utils.h b/src/libtracker-miner/tracker-utils.h
new file mode 100644
index 0000000..f5b643b
--- /dev/null
+++ b/src/libtracker-miner/tracker-utils.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009, Nokia
+ *
+ * This library 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 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ */
+
+#ifndef __TRACKERD_UTILS_H__
+#define __TRACKERD_UTILS_H__
+
+#include <glib.h>
+
+#include "tracker-config.h"
+
+void tracker_throttle (TrackerConfig *config,
+ gint multiplier);
+
+#endif /* __TRACKERD_UTILS_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]