tracker-0.16.2/src/tracker-extract/tracker-controller.c

No issues found

   1 /*
   2  * Copyright (C) 2011, Nokia <ivan.frade@nokia.com>
   3  *
   4  * This library is free software; you can redistribute it and/or
   5  * modify it under the terms of the GNU Lesser General Public
   6  * License as published by the Free Software Foundation; either
   7  * version 2.1 of the License, or (at your option) any later version.
   8  *
   9  * This library is distributed in the hope that it will be useful,
  10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
  12  * Lesser General Public License for more details.
  13  *
  14  * You should have received a copy of the GNU Lesser General Public
  15  * License along with this library; if not, write to the
  16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  17  * Boston, MA  02110-1301, USA.
  18  */
  19 
  20 #include "tracker-controller.h"
  21 #include "tracker-extract.h"
  22 
  23 #include <gio/gunixoutputstream.h>
  24 #include <gio/gunixinputstream.h>
  25 #include <gio/gunixfdlist.h>
  26 
  27 #include <libtracker-common/tracker-common.h>
  28 #include <libtracker-extract/tracker-extract.h>
  29 #include <libtracker-miner/tracker-miner.h>
  30 #include <gio/gio.h>
  31 
  32 #ifdef STAYALIVE_ENABLE_TRACE
  33 #warning Stayalive traces enabled
  34 #endif /* STAYALIVE_ENABLE_TRACE */
  35 
  36 #ifdef THREAD_ENABLE_TRACE
  37 #warning Controller thread traces enabled
  38 #endif /* THREAD_ENABLE_TRACE */
  39 
  40 #define WATCHDOG_TIMEOUT 20
  41 
  42 typedef struct TrackerControllerPrivate TrackerControllerPrivate;
  43 typedef struct GetMetadataData GetMetadataData;
  44 
  45 struct TrackerControllerPrivate {
  46 	GMainContext *context;
  47 	GMainLoop *main_loop;
  48 
  49 	TrackerStorage *storage;
  50 	TrackerExtract *extractor;
  51 
  52 	GDBusConnection *connection;
  53 	GDBusNodeInfo *introspection_data;
  54 	guint registration_id;
  55 	guint bus_name_id;
  56 
  57 	GList *ongoing_tasks;
  58 
  59 	guint shutdown_timeout;
  60 	GSource *shutdown_source;
  61 
  62 
  63 	GError *initialization_error;
  64 #if GLIB_CHECK_VERSION (2,31,0)
  65 	GCond initialization_cond;
  66 	GMutex initialization_mutex;
  67 #else
  68 	GCond *initialization_cond;
  69 	GMutex *initialization_mutex;
  70 #endif
  71 
  72 	guint initialized : 1;
  73 };
  74 
  75 struct GetMetadataData {
  76 	TrackerController *controller;
  77 	GCancellable *cancellable;
  78 	GDBusMethodInvocation *invocation;
  79 	TrackerDBusRequest *request;
  80 	gchar *uri;
  81 	gchar *mimetype;
  82 	gint fd; /* Only for fast queries */
  83 
  84 	GSource *watchdog_source;
  85 };
  86 
  87 #define TRACKER_EXTRACT_SERVICE   "org.freedesktop.Tracker1.Extract"
  88 #define TRACKER_EXTRACT_PATH      "/org/freedesktop/Tracker1/Extract"
  89 #define TRACKER_EXTRACT_INTERFACE "org.freedesktop.Tracker1.Extract"
  90 
  91 #define MAX_EXTRACT_TIME 10
  92 
  93 static const gchar *introspection_xml =
  94 	"<node>"
  95 	"  <interface name='org.freedesktop.Tracker1.Extract'>"
  96 	"    <method name='GetPid'>"
  97 	"      <arg type='i' name='value' direction='out' />"
  98 	"    </method>"
  99 	"    <method name='GetMetadata'>"
 100 	"      <arg type='s' name='uri' direction='in' />"
 101 	"      <arg type='s' name='mime' direction='in' />"
 102 	"      <arg type='s' name='graph' direction='in' />"
 103 	"      <arg type='s' name='preupdate' direction='out' />"
 104 	"      <arg type='s' name='postupdate' direction='out' />"
 105 	"      <arg type='s' name='embedded' direction='out' />"
 106 	"      <arg type='s' name='where' direction='out' />"
 107 	"    </method>"
 108 	"    <method name='GetMetadataFast'>"
 109 	"      <arg type='s' name='uri' direction='in' />"
 110 	"      <arg type='s' name='mime' direction='in' />"
 111 	"      <arg type='s' name='graph' direction='in' />"
 112 	"      <arg type='h' name='fd' direction='in' />"
 113 	"    </method>"
 114 	"    <method name='CancelTasks'>"
 115 	"      <arg type='as' name='uri' direction='in' />"
 116 	"    </method>"
 117 	"  </interface>"
 118 	"</node>";
 119 
 120 enum {
 121 	PROP_0,
 122 	PROP_SHUTDOWN_TIMEOUT,
 123 	PROP_EXTRACTOR
 124 };
 125 
 126 static void	tracker_controller_initable_iface_init  (GInitableIface     *iface);
 127 static gboolean tracker_controller_dbus_start       (TrackerController  *controller,
 128                                                      GError            **error);
 129 static void	tracker_controller_dbus_stop            (TrackerController  *controller);
 130 
 131 
 132 G_DEFINE_TYPE_WITH_CODE (TrackerController, tracker_controller, G_TYPE_OBJECT,
 133                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
 134                                                 tracker_controller_initable_iface_init));
 135 
 136 static gboolean
 137 tracker_controller_initable_init (GInitable     *initable,
 138                                   GCancellable  *cancellable,
 139                                   GError       **error)
 140 {
 141 	return tracker_controller_start (TRACKER_CONTROLLER (initable), error);
 142 }
 143 
 144 static void
 145 tracker_controller_initable_iface_init (GInitableIface *iface)
 146 {
 147 	iface->init = tracker_controller_initable_init;
 148 }
 149 
 150 static void
 151 tracker_controller_finalize (GObject *object)
 152 {
 153 	TrackerControllerPrivate *priv;
 154 	TrackerController *controller;
 155 
 156 	controller = TRACKER_CONTROLLER (object);
 157 	priv = controller->priv;
 158 
 159 	if (priv->shutdown_source) {
 160 		g_source_destroy (priv->shutdown_source);
 161 		priv->shutdown_source = NULL;
 162 	}
 163 
 164 	tracker_controller_dbus_stop (controller);
 165 
 166 	if (priv->extractor) {
 167 		g_object_unref (priv->extractor);
 168 	}
 169 
 170 	g_object_unref (priv->storage);
 171 
 172 	g_main_loop_unref (priv->main_loop);
 173 	g_main_context_unref (priv->context);
 174 
 175 #if GLIB_CHECK_VERSION (2,31,0)
 176 	g_cond_clear (&priv->initialization_cond);
 177 	g_mutex_clear (&priv->initialization_mutex);
 178 #else
 179 	g_cond_free (priv->initialization_cond);
 180 	g_mutex_free (priv->initialization_mutex);
 181 #endif
 182 
 183 	G_OBJECT_CLASS (tracker_controller_parent_class)->finalize (object);
 184 }
 185 
 186 static void
 187 tracker_controller_get_property (GObject    *object,
 188                                  guint       param_id,
 189                                  GValue     *value,
 190                                  GParamSpec *pspec)
 191 {
 192 	TrackerControllerPrivate *priv = TRACKER_CONTROLLER (object)->priv;
 193 
 194 	switch (param_id) {
 195 	case PROP_SHUTDOWN_TIMEOUT:
 196 		g_value_set_uint (value, priv->shutdown_timeout);
 197 		break;
 198 	case PROP_EXTRACTOR:
 199 		g_value_set_object (value, priv->extractor);
 200 		break;
 201 	}
 202 }
 203 
 204 static void
 205 tracker_controller_set_property (GObject      *object,
 206                                  guint         param_id,
 207                                  const GValue *value,
 208                                  GParamSpec   *pspec)
 209 {
 210 	TrackerControllerPrivate *priv = TRACKER_CONTROLLER (object)->priv;
 211 
 212 	switch (param_id) {
 213 	case PROP_SHUTDOWN_TIMEOUT:
 214 		priv->shutdown_timeout = g_value_get_uint (value);
 215 		break;
 216 	case PROP_EXTRACTOR:
 217 		priv->extractor = g_value_dup_object (value);
 218 		break;
 219 	}
 220 }
 221 
 222 static void
 223 tracker_controller_class_init (TrackerControllerClass *klass)
 224 {
 225 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 226 
 227 	object_class->finalize = tracker_controller_finalize;
 228 	object_class->get_property = tracker_controller_get_property;
 229 	object_class->set_property = tracker_controller_set_property;
 230 
 231 	g_object_class_install_property (object_class,
 232 	                                 PROP_SHUTDOWN_TIMEOUT,
 233 	                                 g_param_spec_uint ("shutdown-timeout",
 234 	                                                    "Shutdown timeout",
 235 	                                                    "Shutdown timeout, 0 to disable",
 236 	                                                    0, 1000, 0,
 237 	                                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 238 	g_object_class_install_property (object_class,
 239 	                                 PROP_EXTRACTOR,
 240 	                                 g_param_spec_object ("extractor",
 241 	                                                      "Extractor",
 242 	                                                      "Extractor",
 243 	                                                      TRACKER_TYPE_EXTRACT,
 244 	                                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 245 
 246 	g_type_class_add_private (object_class, sizeof (TrackerControllerPrivate));
 247 }
 248 
 249 static GSource *
 250 controller_timeout_source_new (guint       interval,
 251                                GSourceFunc func,
 252                                gpointer    user_data)
 253 {
 254 	GMainContext *context;
 255 	GSource *source;
 256 
 257 	context = g_main_context_get_thread_default ();
 258 
 259 	source = g_timeout_source_new_seconds (interval);
 260 	g_source_set_callback (source, func, user_data, NULL);
 261 	g_source_attach (source, context);
 262 
 263 	return source;
 264 }
 265 
 266 static gboolean
 267 watchdog_timeout_cb (gpointer user_data)
 268 {
 269 	GetMetadataData *data = user_data;
 270 	TrackerControllerPrivate *priv = data->controller->priv;
 271 
 272 	g_critical ("Extraction task for '%s' went rogue and took more than %d seconds. Forcing exit.",
 273 	            data->uri, WATCHDOG_TIMEOUT);
 274 
 275 	g_main_loop_quit (priv->main_loop);
 276 
 277 	return FALSE;
 278 }
 279 
 280 static GetMetadataData *
 281 metadata_data_new (TrackerController     *controller,
 282                    const gchar           *uri,
 283                    const gchar           *mime,
 284                    GDBusMethodInvocation *invocation,
 285                    TrackerDBusRequest    *request)
 286 {
 287 	GetMetadataData *data;
 288 
 289 	data = g_slice_new (GetMetadataData);
 290 	data->cancellable = g_cancellable_new ();
 291 	data->controller = controller;
 292 	data->uri = g_strdup (uri);
 293 	data->mimetype = g_strdup (mime);
 294 	data->invocation = invocation;
 295 	data->request = request;
 296 
 297 	data->watchdog_source = controller_timeout_source_new (WATCHDOG_TIMEOUT,
 298 	                                                       watchdog_timeout_cb,
 299 	                                                       data);
 300 	return data;
 301 }
 302 
 303 static void
 304 metadata_data_free (GetMetadataData *data)
 305 {
 306 	/* We rely on data->invocation being freed through
 307 	 * the g_dbus_method_invocation_return_* methods
 308 	 */
 309 	g_free (data->uri);
 310 	g_free (data->mimetype);
 311 	g_object_unref (data->cancellable);
 312 	g_source_destroy (data->watchdog_source);
 313 	g_slice_free (GetMetadataData, data);
 314 }
 315 
 316 static void
 317 cancel_tasks_in_file (TrackerController *controller,
 318 		      GFile             *file)
 319 {
 320 	TrackerControllerPrivate *priv;
 321 	GList *elem;
 322 
 323 	priv = controller->priv;
 324 
 325 	for (elem = priv->ongoing_tasks; elem; elem = elem->next) {
 326 		GetMetadataData *data;
 327 		GFile *task_file;
 328 
 329 		data = elem->data;
 330 		task_file = g_file_new_for_uri (data->uri);
 331 
 332 		if (g_file_equal (task_file, file) ||
 333 		    g_file_has_prefix (task_file, file)) {
 334 			/* Mount path contains some file being processed */
 335 			g_message ("Cancelling task ('%s')", data->uri);
 336 			g_cancellable_cancel (data->cancellable);
 337 		}
 338 
 339 		g_object_unref (task_file);
 340 	}
 341 }
 342 
 343 static void
 344 mount_point_removed_cb (TrackerStorage *storage,
 345                         const gchar    *uuid,
 346                         const gchar    *mount_point,
 347                         gpointer        user_data)
 348 {
 349 	GFile *mount_file;
 350 
 351 	mount_file = g_file_new_for_path (mount_point);
 352 	cancel_tasks_in_file (TRACKER_CONTROLLER (user_data), mount_file);
 353 	g_object_unref (mount_file);
 354 }
 355 
 356 static gboolean
 357 reset_shutdown_timeout_cb (gpointer user_data)
 358 {
 359 	TrackerControllerPrivate *priv;
 360 
 361 #ifdef STAYALIVE_ENABLE_TRACE
 362 	g_debug ("Stayalive --- time has expired");
 363 #endif /* STAYALIVE_ENABLE_TRACE */
 364 
 365 	g_message ("Shutting down due to no activity");
 366 
 367 	priv = TRACKER_CONTROLLER (user_data)->priv;
 368 	g_main_loop_quit (priv->main_loop);
 369 
 370 	return FALSE;
 371 }
 372 
 373 static void
 374 reset_shutdown_timeout (TrackerController *controller)
 375 {
 376 	TrackerControllerPrivate *priv;
 377 
 378 	priv = controller->priv;
 379 
 380 	if (priv->shutdown_timeout == 0) {
 381 		return;
 382 	}
 383 
 384 #ifdef STAYALIVE_ENABLE_TRACE
 385 	g_debug ("Stayalive --- (Re)setting timeout");
 386 #endif /* STAYALIVE_ENABLE_TRACE */
 387 
 388 	if (priv->shutdown_source) {
 389 		g_source_destroy (priv->shutdown_source);
 390 		priv->shutdown_source = NULL;
 391 	}
 392 
 393 	priv->shutdown_source = controller_timeout_source_new (priv->shutdown_timeout,
 394 	                                                       reset_shutdown_timeout_cb,
 395 	                                                       controller);
 396 }
 397 
 398 static void
 399 tracker_controller_init (TrackerController *controller)
 400 {
 401 	TrackerControllerPrivate *priv;
 402 
 403 	priv = controller->priv = G_TYPE_INSTANCE_GET_PRIVATE (controller,
 404 	                                                       TRACKER_TYPE_CONTROLLER,
 405 	                                                       TrackerControllerPrivate);
 406 
 407 	priv->context = g_main_context_new ();
 408 	priv->main_loop = g_main_loop_new (priv->context, FALSE);
 409 
 410 	priv->storage = tracker_storage_new ();
 411 	g_signal_connect (priv->storage, "mount-point-removed",
 412 	                  G_CALLBACK (mount_point_removed_cb), controller);
 413 
 414 #if GLIB_CHECK_VERSION (2,31,0)
 415 	g_cond_init (&priv->initialization_cond);
 416 	g_mutex_init (&priv->initialization_mutex);
 417 #else
 418 	priv->initialization_cond = g_cond_new ();
 419 	priv->initialization_mutex = g_mutex_new ();
 420 #endif
 421 }
 422 
 423 static void
 424 handle_method_call_get_pid (TrackerController     *controller,
 425                             GDBusMethodInvocation *invocation,
 426                             GVariant              *parameters)
 427 {
 428 	TrackerDBusRequest *request;
 429 	pid_t value;
 430 
 431 	request = tracker_g_dbus_request_begin (invocation,
 432 	                                        "%s()",
 433 	                                        __FUNCTION__);
 434 
 435 	reset_shutdown_timeout (controller);
 436 	value = getpid ();
 437 	tracker_dbus_request_debug (request,
 438 	                            "PID is %d",
 439 	                            value);
 440 
 441 	tracker_dbus_request_end (request, NULL);
 442 
 443 	g_dbus_method_invocation_return_value (invocation,
 444 	                                       g_variant_new ("(i)", (gint) value));
 445 }
 446 
 447 static void
 448 get_metadata_cb (GObject      *object,
 449                  GAsyncResult *res,
 450                  gpointer      user_data)
 451 {
 452 	TrackerControllerPrivate *priv;
 453 	GetMetadataData *data;
 454 	TrackerExtractInfo *info;
 455 
 456 	data = user_data;
 457 	priv = data->controller->priv;
 458 	priv->ongoing_tasks = g_list_remove (priv->ongoing_tasks, data);
 459 	info = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
 460 
 461 	if (info) {
 462 		const gchar *preupdate, *postupdate, *statements, *where;
 463 		TrackerSparqlBuilder *builder;
 464 
 465 		builder = tracker_extract_info_get_preupdate_builder (info);
 466 		preupdate = tracker_sparql_builder_get_result (builder);
 467 
 468 		builder = tracker_extract_info_get_postupdate_builder (info);
 469 		postupdate = tracker_sparql_builder_get_result (builder);
 470 
 471 		builder = tracker_extract_info_get_metadata_builder (info);
 472 		statements = tracker_sparql_builder_get_result (builder);
 473 
 474 		where = tracker_extract_info_get_where_clause (info);
 475 
 476 		if (statements && *statements) {
 477 			g_dbus_method_invocation_return_value (data->invocation,
 478 			                                       g_variant_new ("(ssss)",
 479 			                                                      preupdate ? preupdate : "",
 480 			                                                      postupdate ? postupdate : "",
 481 			                                                      statements,
 482 			                                                      where ? where : ""));
 483 		} else {
 484 			g_dbus_method_invocation_return_value (data->invocation,
 485 			                                       g_variant_new ("(ssss)", "", "", "", ""));
 486 		}
 487 
 488 		tracker_dbus_request_end (data->request, NULL);
 489 	} else {
 490 		GError *error = NULL;
 491 
 492 
 493 #ifdef THREAD_ENABLE_TRACE
 494 		g_debug ("Thread:%p (Controller) --> Got error back",
 495 		         g_thread_self ());
 496 #endif /* THREAD_ENABLE_TRACE */
 497 
 498 		g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), &error);
 499 		tracker_dbus_request_end (data->request, error);
 500 		g_dbus_method_invocation_return_gerror (data->invocation, error);
 501 		g_error_free (error);
 502 	}
 503 
 504 	metadata_data_free (data);
 505 }
 506 
 507 static void
 508 handle_method_call_get_metadata (TrackerController     *controller,
 509                                  GDBusMethodInvocation *invocation,
 510                                  GVariant              *parameters)
 511 {
 512 	TrackerControllerPrivate *priv;
 513 	GetMetadataData *data;
 514 	TrackerDBusRequest *request;
 515 	const gchar *uri, *mime, *graph;
 516 
 517 	priv = controller->priv;
 518 	g_variant_get (parameters, "(&s&s&s)", &uri, &mime, &graph);
 519 
 520 	reset_shutdown_timeout (controller);
 521 	request = tracker_dbus_request_begin (NULL, "%s (%s, %s)", __FUNCTION__, uri, mime);
 522 
 523 	data = metadata_data_new (controller, uri, mime, invocation, request);
 524 	tracker_extract_file (priv->extractor, uri, mime, graph,
 525 	                      data->cancellable,
 526 	                      get_metadata_cb, data);
 527 	priv->ongoing_tasks = g_list_prepend (priv->ongoing_tasks, data);
 528 }
 529 
 530 static void
 531 handle_method_call_cancel_tasks (TrackerController     *controller,
 532                                  GDBusMethodInvocation *invocation,
 533                                  GVariant              *parameters)
 534 {
 535 	TrackerDBusRequest *request;
 536 	gchar **uris;
 537 	gint i;
 538 
 539 #ifdef THREAD_ENABLE_TRACE
 540 	g_debug ("Thread:%p (Controller) --> Got Tasks cancellation request",
 541 		 g_thread_self ());
 542 #endif /* THREAD_ENABLE_TRACE */
 543 
 544 
 545 	g_variant_get (parameters, "(^as)", &uris);
 546 
 547 	request = tracker_dbus_request_begin (NULL, "%s (%s, ...)", __FUNCTION__, uris[0]);
 548 
 549 	for (i = 0; uris[i] != NULL; i++) {
 550 		GFile *file;
 551 
 552 		file = g_file_new_for_uri (uris[i]);
 553 		cancel_tasks_in_file (controller, file);
 554 		g_object_unref (file);
 555 	}
 556 
 557 	g_strfreev (uris);
 558 	tracker_dbus_request_end (request, NULL);
 559 	g_dbus_method_invocation_return_value (invocation, NULL);
 560 }
 561 
 562 static inline void
 563 get_metadata_fast_write (GDataOutputStream *data_output_stream,
 564                          const gchar       *string,
 565                          GError            *error)
 566 {
 567 	if (error) {
 568 		return;
 569 	}
 570 
 571 	/* Append data */
 572 	g_data_output_stream_put_string (data_output_stream,
 573 	                                 string ? string : "",
 574 	                                 NULL,
 575 	                                 &error);
 576 
 577 	if (error) {
 578 		return;
 579 	}
 580 
 581 	/* Append a '\0' */
 582 	g_data_output_stream_put_byte (data_output_stream,
 583 	                               0,
 584 	                               NULL,
 585 	                               &error);
 586 }
 587 
 588 static void
 589 get_metadata_fast_cb (GObject      *object,
 590                       GAsyncResult *res,
 591                       gpointer      user_data)
 592 {
 593 	TrackerControllerPrivate *priv;
 594 	GetMetadataData *data;
 595 	TrackerExtractInfo *info;
 596 
 597 	data = user_data;
 598 	priv = data->controller->priv;
 599 	priv->ongoing_tasks = g_list_remove (priv->ongoing_tasks, data);
 600 	info = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
 601 
 602 	if (info) {
 603 		GOutputStream *unix_output_stream;
 604 		GOutputStream *buffered_output_stream;
 605 		GDataOutputStream *data_output_stream;
 606 		const gchar *preupdate, *postupdate, *statements, *where;
 607 		TrackerSparqlBuilder *builder;
 608 		GError *error = NULL;
 609 
 610 #ifdef THREAD_ENABLE_TRACE
 611 		g_debug ("Thread:%p (Controller) --> Got metadata back",
 612 		         g_thread_self ());
 613 #endif /* THREAD_ENABLE_TRACE */
 614 
 615 		unix_output_stream = g_unix_output_stream_new (data->fd, TRUE);
 616 		buffered_output_stream = g_buffered_output_stream_new_sized (unix_output_stream,
 617 		                                                             64 * 1024);
 618 		data_output_stream = g_data_output_stream_new (buffered_output_stream);
 619 		g_data_output_stream_set_byte_order (G_DATA_OUTPUT_STREAM (data_output_stream),
 620 		                                     G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN);
 621 
 622 		builder = tracker_extract_info_get_preupdate_builder (info);
 623 		preupdate = tracker_sparql_builder_get_result (builder);
 624 
 625 		builder = tracker_extract_info_get_postupdate_builder (info);
 626 		postupdate = tracker_sparql_builder_get_result (builder);
 627 
 628 		builder = tracker_extract_info_get_metadata_builder (info);
 629 		statements = tracker_sparql_builder_get_result (builder);
 630 
 631 		where = tracker_extract_info_get_where_clause (info);
 632 
 633 		/* So the structure is like this:
 634 		 *
 635 		 *   [buffer,'\0'][buffer,'\0'][...]
 636 		 *
 637 		 * We avoid strlen() using
 638 		 * g_data_input_stream_read_upto() and the
 639 		 * NUL-terminating byte given strlen() has a size_t
 640 		 * limitation and costs us time evaluating string
 641 		 * lengths.
 642 		 */
 643 		if (statements && *statements) {
 644 			get_metadata_fast_write (data_output_stream, preupdate, error);
 645 			get_metadata_fast_write (data_output_stream, postupdate, error);
 646 			get_metadata_fast_write (data_output_stream, statements, error);
 647 			get_metadata_fast_write (data_output_stream, where, error);
 648 		}
 649 
 650 		g_object_unref (data_output_stream);
 651 		g_object_unref (buffered_output_stream);
 652 		g_object_unref (unix_output_stream);
 653 
 654 		if (error) {
 655 			tracker_dbus_request_end (data->request, error);
 656 			g_dbus_method_invocation_return_gerror (data->invocation, error);
 657 			g_error_free (error);
 658 		} else {
 659 			tracker_dbus_request_end (data->request, NULL);
 660 			g_dbus_method_invocation_return_value (data->invocation, NULL);
 661 		}
 662 	} else {
 663 		GError *error = NULL;
 664 
 665 #ifdef THREAD_ENABLE_TRACE
 666 		g_debug ("Thread:%p (Controller) --> Got error back",
 667 		         g_thread_self ());
 668 #endif /* THREAD_ENABLE_TRACE */
 669 
 670 		g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), &error);
 671 		tracker_dbus_request_end (data->request, error);
 672 		g_dbus_method_invocation_return_gerror (data->invocation, error);
 673 		g_error_free (error);
 674 
 675 		close (data->fd);
 676 	}
 677 
 678 	metadata_data_free (data);
 679 }
 680 
 681 static void
 682 handle_method_call_get_metadata_fast (TrackerController     *controller,
 683                                       GDBusMethodInvocation *invocation,
 684                                       GVariant              *parameters)
 685 {
 686 	GDBusConnection *connection;
 687 	GDBusMessage *method_message;
 688 	TrackerDBusRequest *request;
 689 
 690 	connection = g_dbus_method_invocation_get_connection (invocation);
 691 	method_message = g_dbus_method_invocation_get_message (invocation);
 692 
 693 	if (g_dbus_connection_get_capabilities (connection) & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING) {
 694 		TrackerControllerPrivate *priv;
 695 		GetMetadataData *data;
 696 		const gchar *uri, *mime, *graph;
 697 		gint index_fd, fd;
 698 		GUnixFDList *fd_list;
 699 		GError *error = NULL;
 700 
 701 		priv = controller->priv;
 702 
 703 		g_variant_get (parameters, "(&s&s&sh)",
 704 		               &uri, &mime, &graph, &index_fd);
 705 
 706 		request = tracker_dbus_request_begin (NULL,
 707 		                                      "%s (uri:'%s', mime:'%s', index_fd:%d)",
 708 		                                      __FUNCTION__,
 709 		                                      uri,
 710 		                                      mime,
 711 		                                      index_fd);
 712 		reset_shutdown_timeout (controller);
 713 
 714 		fd_list = g_dbus_message_get_unix_fd_list (method_message);
 715 
 716 		if (fd_list == NULL) {
 717 			error = g_error_new_literal (TRACKER_DBUS_ERROR, 0,
 718 				                     "No FD list");
 719 		}
 720 
 721 		if (fd_list && (fd = g_unix_fd_list_get (fd_list, index_fd, &error)) != -1) {
 722 			data = metadata_data_new (controller, uri, mime, invocation, request);
 723 			data->fd = fd;
 724 
 725 			tracker_extract_file (priv->extractor, uri, mime, graph,
 726 			                      data->cancellable,
 727 			                      get_metadata_fast_cb, data);
 728 			priv->ongoing_tasks = g_list_prepend (priv->ongoing_tasks, data);
 729 		} else {
 730 			tracker_dbus_request_end (request, error);
 731 			g_dbus_method_invocation_return_dbus_error (invocation,
 732 			                                            TRACKER_EXTRACT_SERVICE ".GetMetadataFastError",
 733 			                                            "No FD list");
 734 			g_error_free (error);
 735 		}
 736 	} else {
 737 		request = tracker_dbus_request_begin (NULL,
 738 		                                      "%s (uri:'n/a', mime:'n/a', index_fd:unknown)",
 739 		                                      __FUNCTION__);
 740 		reset_shutdown_timeout (controller);
 741 		tracker_dbus_request_end (request, NULL);
 742 		g_dbus_method_invocation_return_dbus_error (invocation,
 743 		                                            TRACKER_EXTRACT_SERVICE ".GetMetadataFastError",
 744 		                                            "No FD passing capabilities");
 745 	}
 746 }
 747 
 748 static void
 749 handle_method_call (GDBusConnection       *connection,
 750                     const gchar           *sender,
 751                     const gchar           *object_path,
 752                     const gchar           *interface_name,
 753                     const gchar           *method_name,
 754                     GVariant              *parameters,
 755                     GDBusMethodInvocation *invocation,
 756                     gpointer               user_data)
 757 {
 758 	TrackerController *controller = user_data;
 759 
 760 	if (g_strcmp0 (method_name, "GetPid") == 0) {
 761 		handle_method_call_get_pid (controller, invocation, parameters);
 762 	} else if (g_strcmp0 (method_name, "GetMetadataFast") == 0) {
 763 		handle_method_call_get_metadata_fast (controller, invocation, parameters);
 764 	} else if (g_strcmp0 (method_name, "GetMetadata") == 0) {
 765 		handle_method_call_get_metadata (controller, invocation, parameters);
 766 	} else if (g_strcmp0 (method_name, "CancelTasks") == 0) {
 767 		handle_method_call_cancel_tasks (controller, invocation, parameters);
 768 	} else {
 769 		g_warning ("Unknown method '%s' called", method_name);
 770 	}
 771 }
 772 
 773 static void
 774 controller_notify_main_thread (TrackerController *controller,
 775                                GError            *error)
 776 {
 777 	TrackerControllerPrivate *priv;
 778 
 779 	priv = controller->priv;
 780 
 781 #if GLIB_CHECK_VERSION (2,31,0)
 782 	g_mutex_lock (&priv->initialization_mutex);
 783 #else
 784 	g_mutex_lock (priv->initialization_mutex);
 785 #endif
 786 
 787 	priv->initialized = TRUE;
 788 	priv->initialization_error = error;
 789 
 790 	/* Notify about the initialization */
 791 #if GLIB_CHECK_VERSION (2,31,0)
 792 	g_cond_signal (&priv->initialization_cond);
 793 	g_mutex_unlock (&priv->initialization_mutex);
 794 #else
 795 	g_cond_signal (priv->initialization_cond);
 796 	g_mutex_unlock (priv->initialization_mutex);
 797 #endif
 798 }
 799 
 800 static void
 801 bus_name_acquired_cb (GDBusConnection *connection,
 802                       const gchar     *name,
 803                       gpointer         user_data)
 804 {
 805 	controller_notify_main_thread (TRACKER_CONTROLLER (user_data), NULL);
 806 }
 807 
 808 static void
 809 bus_name_vanished_cb (GDBusConnection *connection,
 810                       const gchar     *name,
 811                       gpointer         user_data)
 812 {
 813 	TrackerController *controller;
 814 	TrackerControllerPrivate *priv;
 815 
 816 	controller = user_data;
 817 	priv = controller->priv;
 818 
 819 	if (!priv->initialized) {
 820 		GError *error;
 821 
 822 		error = g_error_new_literal (TRACKER_DBUS_ERROR, 0,
 823 		                             "Could not acquire bus name, "
 824 		                             "perhaps it's already taken?");
 825 		controller_notify_main_thread (controller, error);
 826 	} else {
 827 		/* We're already in control of the program
 828 		 * lifetime, so just quit the mainloop
 829 		 */
 830 		g_main_loop_quit (priv->main_loop);
 831 	}
 832 }
 833 
 834 static gboolean
 835 tracker_controller_dbus_start (TrackerController   *controller,
 836                                GError             **error)
 837 {
 838 	TrackerControllerPrivate *priv;
 839 	GError *err = NULL;
 840 	GDBusInterfaceVTable interface_vtable = {
 841 		handle_method_call,
 842 		NULL, NULL
 843 	};
 844 
 845 	priv = controller->priv;
 846 	priv->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &err);
 847 
 848 	if (!priv->connection) {
 849 		g_critical ("Could not connect to the D-Bus session bus, %s",
 850 		            err ? err->message : "no error given.");
 851 		g_propagate_error (error, err);
 852 		return FALSE;
 853 	}
 854 
 855 	priv->introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, &err);
 856 	if (!priv->introspection_data) {
 857 		g_critical ("Could not create node info from introspection XML, %s",
 858 		            err ? err->message : "no error given.");
 859 		g_propagate_error (error, err);
 860 		return FALSE;
 861 	}
 862 
 863 	g_message ("Registering D-Bus object...");
 864 	g_message ("  Path:'" TRACKER_EXTRACT_PATH "'");
 865 	g_message ("  Object Type:'%s'", G_OBJECT_TYPE_NAME (controller));
 866 
 867 	priv->registration_id =
 868 		g_dbus_connection_register_object (priv->connection,
 869 		                                   TRACKER_EXTRACT_PATH,
 870 		                                   priv->introspection_data->interfaces[0],
 871 		                                   &interface_vtable,
 872 		                                   controller,
 873 		                                   NULL,
 874 		                                   &err);
 875 
 876 	if (err) {
 877 		g_critical ("Could not register the D-Bus object "TRACKER_EXTRACT_PATH", %s",
 878 		            err ? err->message : "no error given.");
 879 		g_propagate_error (error, err);
 880 		return FALSE;
 881 	}
 882 
 883 	priv->bus_name_id =
 884 		g_bus_own_name_on_connection (priv->connection,
 885 		                              TRACKER_EXTRACT_SERVICE,
 886 		                              G_BUS_NAME_OWNER_FLAGS_NONE,
 887 		                              bus_name_acquired_cb,
 888 		                              bus_name_vanished_cb,
 889 		                              controller, NULL);
 890 
 891 	if (err) {
 892 		g_critical ("Could not own the D-Bus name "TRACKER_EXTRACT_SERVICE", %s",
 893 		            err ? err->message : "no error given.");
 894 		g_propagate_error (error, err);
 895 		return FALSE;
 896 	}
 897 
 898 	return TRUE;
 899 }
 900 
 901 static void
 902 tracker_controller_dbus_stop (TrackerController *controller)
 903 {
 904 	TrackerControllerPrivate *priv;
 905 
 906 	priv = controller->priv;
 907 
 908 	if (priv->registration_id != 0) {
 909 		g_dbus_connection_unregister_object (priv->connection,
 910 		                                     priv->registration_id);
 911 	}
 912 
 913 	if (priv->bus_name_id != 0) {
 914 		g_bus_unown_name (priv->bus_name_id);
 915 	}
 916 
 917 	if (priv->introspection_data) {
 918 		g_dbus_node_info_unref (priv->introspection_data);
 919 	}
 920 
 921 	if (priv->connection) {
 922 		g_object_unref (priv->connection);
 923 	}
 924 }
 925 
 926 TrackerController *
 927 tracker_controller_new (TrackerExtract   *extractor,
 928                         guint             shutdown_timeout,
 929                         GError          **error)
 930 {
 931 	return g_initable_new (TRACKER_TYPE_CONTROLLER,
 932 	                       NULL, error,
 933 	                       "extractor", extractor,
 934 	                       "shutdown-timeout", shutdown_timeout,
 935 	                       NULL);
 936 }
 937 
 938 static gpointer
 939 tracker_controller_thread_func (gpointer user_data)
 940 {
 941 	TrackerController *controller;
 942 	TrackerControllerPrivate *priv;
 943 	GError *error = NULL;
 944 
 945 #ifdef THREAD_ENABLE_TRACE
 946 	g_debug ("Thread:%p (Controller) --- Created, dispatching...",
 947 	         g_thread_self ());
 948 #endif /* THREAD_ENABLE_TRACE */
 949 
 950 	controller = user_data;
 951 	priv = controller->priv;
 952 	g_main_context_push_thread_default (priv->context);
 953 
 954 	reset_shutdown_timeout (controller);
 955 
 956 	if (!tracker_controller_dbus_start (controller, &error)) {
 957 		/* Error has been filled in, so we return
 958 		 * in this thread. The main thread will be
 959 		 * notified about the error and exit.
 960 		 */
 961 		controller_notify_main_thread (controller, error);
 962 		return NULL;
 963 	}
 964 
 965 	g_main_loop_run (priv->main_loop);
 966 
 967 #ifdef THREAD_ENABLE_TRACE
 968 	g_debug ("Thread:%p (Controller) --- Shutting down...",
 969 	         g_thread_self ());
 970 #endif /* THREAD_ENABLE_TRACE */
 971 
 972 	g_object_unref (controller);
 973 
 974 	/* This is where tracker-extract exits, be it
 975 	 * either through umount events on monitored
 976 	 * files' volumes or the timeout being reached
 977 	 */
 978 	exit (0);
 979 	return NULL;
 980 }
 981 
 982 gboolean
 983 tracker_controller_start (TrackerController  *controller,
 984                           GError            **error)
 985 {
 986 	TrackerControllerPrivate *priv;
 987 
 988 #if GLIB_CHECK_VERSION (2,31,0)
 989 	GThread *thread;
 990 
 991 	thread = g_thread_try_new ("controller",
 992 	                           tracker_controller_thread_func,
 993 	                           controller,
 994 	                           error);
 995 	if (!thread)
 996 		return FALSE;
 997 
 998 	/* We don't want to join it, so just unref the GThread */
 999 	g_thread_unref (thread);
1000 #else
1001 	if (!g_thread_create (tracker_controller_thread_func,
1002 	                      controller, FALSE, error)) {
1003 		return FALSE;
1004 	}
1005 #endif
1006 
1007 	priv = controller->priv;
1008 
1009 #ifdef THREAD_ENABLE_TRACE
1010 	g_debug ("Thread:%p (Controller) --- Waiting for controller thread to initialize...",
1011 	         g_thread_self ());
1012 #endif /* THREAD_ENABLE_TRACE */
1013 
1014 	/* Wait for the controller thread to notify initialization */
1015 #if GLIB_CHECK_VERSION (2,31,0)
1016 	g_mutex_lock (&priv->initialization_mutex);
1017 	while (!priv->initialized)
1018 		g_cond_wait (&priv->initialization_cond, &priv->initialization_mutex);
1019 	g_mutex_unlock (&priv->initialization_mutex);
1020 #else
1021 	g_mutex_lock (priv->initialization_mutex);
1022 	while (!priv->initialized) {
1023 		g_cond_wait (priv->initialization_cond, priv->initialization_mutex);
1024 	}
1025 	g_mutex_unlock (priv->initialization_mutex);
1026 #endif
1027 
1028 	/* If there was any error resulting from initialization, propagate it */
1029 	if (priv->initialization_error != NULL) {
1030 		g_propagate_error (error, priv->initialization_error);
1031 		return FALSE;
1032 	}
1033 
1034 #ifdef THREAD_ENABLE_TRACE
1035 	g_debug ("Thread:%p (Controller) --- Initialized",
1036 	         g_thread_self ());
1037 #endif /* THREAD_ENABLE_TRACE */
1038 
1039 	return TRUE;
1040 }