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 }