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 General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 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 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU 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-writeback.h"
21 #include "tracker-writeback-module.h"
22
23 #include <libtracker-common/tracker-common.h>
24 #include <libtracker-miner/tracker-miner.h>
25 #include <libtracker-sparql/tracker-sparql.h>
26
27 #include <gio/gio.h>
28
29 #ifdef STAYALIVE_ENABLE_TRACE
30 #warning Stayalive traces enabled
31 #endif /* STAYALIVE_ENABLE_TRACE */
32
33 #ifdef THREAD_ENABLE_TRACE
34 #warning Controller thread traces enabled
35 #endif /* THREAD_ENABLE_TRACE */
36
37 typedef struct {
38 TrackerController *controller;
39 GCancellable *cancellable;
40 GDBusMethodInvocation *invocation;
41 TrackerDBusRequest *request;
42 gchar *subject;
43 GPtrArray *results;
44 TrackerSparqlConnection *connection;
45 GList *writeback_handlers;
46 guint cancel_id;
47 GError *error;
48 } WritebackData;
49
50 typedef struct {
51 GMainContext *context;
52 GMainLoop *main_loop;
53
54 TrackerStorage *storage;
55
56 GDBusConnection *d_connection;
57 GDBusNodeInfo *introspection_data;
58 guint registration_id;
59 guint bus_name_id;
60
61 GList *ongoing_tasks;
62
63 guint shutdown_timeout;
64 GSource *shutdown_source;
65
66 #if GLIB_CHECK_VERSION (2,31,0)
67 GCond initialization_cond;
68 GMutex initialization_mutex, mutex;
69 #else
70 GCond *initialization_cond;
71 GMutex *initialization_mutex, *mutex;
72 #endif
73 GError *initialization_error;
74
75 guint initialized : 1;
76
77 GHashTable *modules;
78 TrackerSparqlConnection *connection;
79 WritebackData *current;
80 } TrackerControllerPrivate;
81
82 #define TRACKER_WRITEBACK_SERVICE "org.freedesktop.Tracker1.Writeback"
83 #define TRACKER_WRITEBACK_PATH "/org/freedesktop/Tracker1/Writeback"
84 #define TRACKER_WRITEBACK_INTERFACE "org.freedesktop.Tracker1.Writeback"
85
86 static const gchar *introspection_xml =
87 "<node>"
88 " <interface name='org.freedesktop.Tracker1.Writeback'>"
89 " <method name='GetPid'>"
90 " <arg type='i' name='value' direction='out' />"
91 " </method>"
92 " <method name='PerformWriteback'>"
93 " <arg type='s' name='subject' direction='in' />"
94 " <arg type='as' name='rdf_types' direction='in' />"
95 " <arg type='aas' name='results' direction='in' />"
96 " </method>"
97 " <method name='CancelTasks'>"
98 " <arg type='as' name='subjects' direction='in' />"
99 " </method>"
100 " </interface>"
101 "</node>";
102
103 enum {
104 PROP_0,
105 PROP_SHUTDOWN_TIMEOUT,
106 };
107
108 static void tracker_controller_initable_iface_init (GInitableIface *iface);
109 static gboolean tracker_controller_dbus_start (TrackerController *controller,
110 GError **error);
111 static void tracker_controller_dbus_stop (TrackerController *controller);
112 static gboolean tracker_controller_start (TrackerController *controller,
113 GError **error);
114
115 G_DEFINE_TYPE_WITH_CODE (TrackerController, tracker_controller, G_TYPE_OBJECT,
116 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
117 tracker_controller_initable_iface_init));
118
119 static gboolean
120 tracker_controller_initable_init (GInitable *initable,
121 GCancellable *cancellable,
122 GError **error)
123 {
124 return tracker_controller_start (TRACKER_CONTROLLER (initable), error);
125 }
126
127 static void
128 tracker_controller_initable_iface_init (GInitableIface *iface)
129 {
130 iface->init = tracker_controller_initable_init;
131 }
132
133 static void
134 tracker_controller_finalize (GObject *object)
135 {
136 TrackerControllerPrivate *priv;
137 TrackerController *controller;
138
139 controller = TRACKER_CONTROLLER (object);
140 priv = controller->priv;
141
142 if (priv->shutdown_source) {
143 g_source_destroy (priv->shutdown_source);
144 priv->shutdown_source = NULL;
145 }
146
147 tracker_controller_dbus_stop (controller);
148
149 g_object_unref (priv->storage);
150 g_hash_table_unref (priv->modules);
151
152 g_main_loop_unref (priv->main_loop);
153 g_main_context_unref (priv->context);
154
155 #if GLIB_CHECK_VERSION (2,31,0)
156 g_cond_clear (&priv->initialization_cond);
157 g_mutex_clear (&priv->initialization_mutex);
158 g_mutex_clear (&priv->mutex);
159 #else
160 g_cond_free (priv->initialization_cond);
161 g_mutex_free (priv->initialization_mutex);
162 g_mutex_free (priv->mutex);
163 #endif
164
165 G_OBJECT_CLASS (tracker_controller_parent_class)->finalize (object);
166 }
167
168 static void
169 tracker_controller_get_property (GObject *object,
170 guint param_id,
171 GValue *value,
172 GParamSpec *pspec)
173 {
174 TrackerControllerPrivate *priv = TRACKER_CONTROLLER (object)->priv;
175
176 switch (param_id) {
177 case PROP_SHUTDOWN_TIMEOUT:
178 g_value_set_uint (value, priv->shutdown_timeout);
179 break;
180 }
181 }
182
183 static void
184 tracker_controller_set_property (GObject *object,
185 guint param_id,
186 const GValue *value,
187 GParamSpec *pspec)
188 {
189 TrackerControllerPrivate *priv = TRACKER_CONTROLLER (object)->priv;
190
191 switch (param_id) {
192 case PROP_SHUTDOWN_TIMEOUT:
193 priv->shutdown_timeout = g_value_get_uint (value);
194 break;
195 }
196 }
197
198 static void
199 tracker_controller_class_init (TrackerControllerClass *klass)
200 {
201 GObjectClass *object_class = G_OBJECT_CLASS (klass);
202
203 object_class->finalize = tracker_controller_finalize;
204 object_class->get_property = tracker_controller_get_property;
205 object_class->set_property = tracker_controller_set_property;
206
207 g_object_class_install_property (object_class,
208 PROP_SHUTDOWN_TIMEOUT,
209 g_param_spec_uint ("shutdown-timeout",
210 "Shutdown timeout",
211 "Shutdown timeout, 0 to disable",
212 0, 1000, 0,
213 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
214
215 g_type_class_add_private (object_class, sizeof (TrackerControllerPrivate));
216 }
217
218 static void
219 task_cancellable_cancelled_cb (GCancellable *cancellable,
220 WritebackData *data)
221 {
222 TrackerControllerPrivate *priv;
223
224 priv = data->controller->priv;
225
226 #if GLIB_CHECK_VERSION (2,31,0)
227 g_mutex_lock (&priv->mutex);
228 #else
229 g_mutex_lock (priv->mutex);
230 #endif
231
232 if (priv->current == data) {
233 g_message ("Cancelled writeback task for '%s' was currently being "
234 "processed, _exit()ing immediately",
235 data->subject);
236 _exit (0);
237 }
238
239 #if GLIB_CHECK_VERSION (2,31,0)
240 g_mutex_unlock (&priv->mutex);
241 #else
242 g_mutex_unlock (priv->mutex);
243 #endif
244 }
245
246
247 static WritebackData *
248 writeback_data_new (TrackerController *controller,
249 GList *writeback_handlers,
250 TrackerSparqlConnection *connection,
251 const gchar *subject,
252 GPtrArray *results,
253 GDBusMethodInvocation *invocation,
254 TrackerDBusRequest *request)
255 {
256 WritebackData *data;
257
258 data = g_slice_new (WritebackData);
259 data->cancellable = g_cancellable_new ();
260 data->controller = g_object_ref (controller);
261 data->subject = g_strdup (subject);
262 data->results = g_ptr_array_ref (results);
263 data->invocation = invocation;
264 data->connection = g_object_ref (connection);
265 data->writeback_handlers = writeback_handlers;
266 data->request = request;
267 data->error = NULL;
268
269 data->cancel_id = g_cancellable_connect (data->cancellable,
270 G_CALLBACK (task_cancellable_cancelled_cb),
271 data, NULL);
272
273 return data;
274 }
275
276 static void
277 writeback_data_free (WritebackData *data)
278 {
279 /* We rely on data->invocation being freed through
280 * the g_dbus_method_invocation_return_* methods
281 */
282 g_cancellable_disconnect (data->cancellable, data->cancel_id);
283 g_free (data->subject);
284 g_object_unref (data->connection);
285 g_ptr_array_unref (data->results);
286 g_object_unref (data->cancellable);
287
288 g_list_foreach (data->writeback_handlers, (GFunc) g_object_unref, NULL);
289 g_list_free (data->writeback_handlers);
290
291 if (data->error) {
292 g_error_free (data->error);
293 }
294 g_slice_free (WritebackData, data);
295 }
296
297 static void
298 cancel_tasks (TrackerController *controller,
299 const gchar *subject,
300 GFile *file)
301 {
302 TrackerControllerPrivate *priv;
303 GList *elem;
304
305 priv = controller->priv;
306
307 for (elem = priv->ongoing_tasks; elem; elem = elem->next) {
308 WritebackData *data = elem->data;
309
310 if (g_strcmp0 (subject, data->subject) == 0) {
311 g_message ("Cancelling not yet processed task ('%s')",
312 data->subject);
313 g_cancellable_cancel (data->cancellable);
314 }
315
316 if (file) {
317 guint i;
318 for (i = 0; i < data->results->len; i++) {
319 GStrv row = g_ptr_array_index (data->results, i);
320 if (row[0] != NULL) {
321 GFile *task_file;
322 task_file = g_file_new_for_uri (row[0]);
323 if (g_file_equal (task_file, file) ||
324 g_file_has_prefix (task_file, file)) {
325 /* Mount path contains some file being processed */
326 g_message ("Cancelling task ('%s')", row[0]);
327 g_cancellable_cancel (data->cancellable);
328 }
329 g_object_unref (task_file);
330 }
331 }
332 }
333 }
334 }
335
336 static void
337 mount_point_removed_cb (TrackerStorage *storage,
338 const gchar *uuid,
339 const gchar *mount_point,
340 gpointer user_data)
341 {
342 GFile *mount_file;
343
344 mount_file = g_file_new_for_path (mount_point);
345 cancel_tasks (TRACKER_CONTROLLER (user_data), NULL, mount_file);
346 g_object_unref (mount_file);
347 }
348
349 static gboolean
350 reset_shutdown_timeout_cb (gpointer user_data)
351 {
352 TrackerControllerPrivate *priv;
353
354 #ifdef STAYALIVE_ENABLE_TRACE
355 g_debug ("Stayalive --- time has expired");
356 #endif /* STAYALIVE_ENABLE_TRACE */
357
358 g_message ("Shutting down due to no activity");
359
360 priv = TRACKER_CONTROLLER (user_data)->priv;
361 g_main_loop_quit (priv->main_loop);
362
363 return FALSE;
364 }
365
366 static void
367 reset_shutdown_timeout (TrackerController *controller)
368 {
369 TrackerControllerPrivate *priv;
370 GSource *source;
371
372 priv = controller->priv;
373
374 if (priv->shutdown_timeout == 0) {
375 return;
376 }
377
378 #ifdef STAYALIVE_ENABLE_TRACE
379 g_debug ("Stayalive --- (Re)setting timeout");
380 #endif /* STAYALIVE_ENABLE_TRACE */
381
382 if (priv->shutdown_source) {
383 g_source_destroy (priv->shutdown_source);
384 priv->shutdown_source = NULL;
385 }
386
387 source = g_timeout_source_new_seconds (priv->shutdown_timeout);
388 g_source_set_callback (source,
389 reset_shutdown_timeout_cb,
390 controller, NULL);
391
392 g_source_attach (source, priv->context);
393 priv->shutdown_source = source;
394 }
395
396 static void
397 tracker_controller_init (TrackerController *controller)
398 {
399 TrackerControllerPrivate *priv;
400
401 priv = controller->priv = G_TYPE_INSTANCE_GET_PRIVATE (controller,
402 TRACKER_TYPE_CONTROLLER,
403 TrackerControllerPrivate);
404
405 priv->context = g_main_context_new ();
406 priv->main_loop = g_main_loop_new (priv->context, FALSE);
407
408 priv->storage = tracker_storage_new ();
409 g_signal_connect (priv->storage, "mount-point-removed",
410 G_CALLBACK (mount_point_removed_cb), controller);
411
412 #if GLIB_CHECK_VERSION (2,31,0)
413 g_cond_init (&priv->initialization_cond);
414 g_mutex_init (&priv->initialization_mutex);
415 g_mutex_init (&priv->mutex);
416 #else
417 priv->initialization_cond = g_cond_new ();
418 priv->initialization_mutex = g_mutex_new ();
419 priv->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 gboolean
448 perform_writeback_cb (gpointer user_data)
449 {
450 TrackerControllerPrivate *priv;
451 WritebackData *data;
452
453 data = user_data;
454 priv = data->controller->priv;
455 priv->ongoing_tasks = g_list_remove (priv->ongoing_tasks, data);
456
457 if (data->error == NULL) {
458 g_dbus_method_invocation_return_value (data->invocation, NULL);
459 } else {
460 g_dbus_method_invocation_return_gerror (data->invocation, data->error);
461 }
462
463 tracker_dbus_request_end (data->request, NULL);
464
465 #if GLIB_CHECK_VERSION (2,31,0)
466 g_mutex_lock (&priv->mutex);
467 priv->current = NULL;
468 g_mutex_unlock (&priv->mutex);
469 #else
470 g_mutex_lock (priv->mutex);
471 priv->current = NULL;
472 g_mutex_unlock (priv->mutex);
473 #endif
474
475 writeback_data_free (data);
476
477 return FALSE;
478 }
479
480 static gboolean
481 sparql_rdf_types_match (const gchar * const *module_types,
482 const gchar * const *rdf_types)
483 {
484 guint n;
485
486 for (n = 0; rdf_types[n] != NULL; n++) {
487 guint i;
488
489 for (i = 0; module_types[i] != NULL; i++) {
490 if (g_strcmp0 (module_types[i], rdf_types[n]) == 0) {
491 return TRUE;
492 }
493 }
494 }
495
496 return FALSE;
497 }
498
499 static gboolean
500 io_writeback_job (GIOSchedulerJob *job,
501 GCancellable *cancellable,
502 gpointer user_data)
503 {
504 WritebackData *data = user_data;
505 TrackerControllerPrivate *priv = data->controller->priv;
506 GError *error = NULL;
507 gboolean handled = FALSE;
508 GList *writeback_handlers;
509
510 #if GLIB_CHECK_VERSION (2,31,0)
511 g_mutex_lock (&priv->mutex);
512 priv->current = data;
513 g_mutex_unlock (&priv->mutex);
514 #else
515 g_mutex_lock (priv->mutex);
516 priv->current = data;
517 g_mutex_unlock (priv->mutex);
518 #endif
519
520 writeback_handlers = data->writeback_handlers;
521
522 while (writeback_handlers) {
523 handled |= tracker_writeback_update_metadata (writeback_handlers->data,
524 data->results,
525 data->connection,
526 data->cancellable,
527 (error) ? NULL : &error);
528 writeback_handlers = writeback_handlers->next;
529 }
530
531 if (!handled) {
532 if (error) {
533 data->error = error;
534 } else {
535 g_set_error_literal (&data->error,
536 TRACKER_DBUS_ERROR,
537 TRACKER_DBUS_ERROR_UNSUPPORTED,
538 "No writeback modules handled "
539 "successfully this file");
540 }
541 } else {
542 g_clear_error (&error);
543 }
544
545 g_idle_add (perform_writeback_cb, data);
546
547 return FALSE;
548 }
549
550 static void
551 handle_method_call_perform_writeback (TrackerController *controller,
552 GDBusMethodInvocation *invocation,
553 GVariant *parameters)
554 {
555 TrackerControllerPrivate *priv;
556 TrackerDBusRequest *request;
557 const gchar *subject;
558 GPtrArray *results = NULL;
559 GHashTableIter iter;
560 gpointer key, value;
561 GVariantIter *iter1, *iter2, *iter3;
562 GArray *rdf_types_array;
563 GStrv rdf_types;
564 gchar *rdf_type = NULL;
565 GList *writeback_handlers = NULL;
566 WritebackData *data;
567
568 priv = controller->priv;
569
570 results = g_ptr_array_new_with_free_func ((GDestroyNotify) g_strfreev);
571 g_variant_get (parameters, "(&sasaas)", &subject, &iter1, &iter2);
572
573 rdf_types_array = g_array_new (TRUE, TRUE, sizeof (gchar *));
574 while (g_variant_iter_loop (iter1, "&s", &rdf_type)) {
575 g_array_append_val (rdf_types_array, rdf_type);
576 }
577
578 rdf_types = (GStrv) rdf_types_array->data;
579 g_array_free (rdf_types_array, FALSE);
580
581 while (g_variant_iter_loop (iter2, "as", &iter3)) {
582 GArray *row_array = g_array_new (TRUE, TRUE, sizeof (gchar *));
583 gchar *cell = NULL;
584
585 while (g_variant_iter_loop (iter3, "&s", &cell)) {
586 g_array_append_val (row_array, cell);
587 }
588
589 g_ptr_array_add (results, row_array->data);
590 g_array_free (row_array, FALSE);
591 }
592
593 g_variant_iter_free (iter1);
594 g_variant_iter_free (iter2);
595
596 reset_shutdown_timeout (controller);
597 request = tracker_dbus_request_begin (NULL, "%s (%s)", __FUNCTION__, subject);
598
599 g_hash_table_iter_init (&iter, priv->modules);
600
601 while (g_hash_table_iter_next (&iter, &key, &value)) {
602 TrackerWritebackModule *module;
603 const gchar * const *module_types;
604
605 module = value;
606 module_types = tracker_writeback_module_get_rdf_types (module);
607
608 if (sparql_rdf_types_match (module_types, (const gchar * const *) rdf_types)) {
609 TrackerWriteback *writeback;
610
611 g_message (" Updating metadata for subject:'%s' using module:'%s'",
612 subject,
613 module->name);
614
615 writeback = tracker_writeback_module_create (module);
616 writeback_handlers = g_list_prepend (writeback_handlers, writeback);
617 }
618 }
619
620 if (writeback_handlers != NULL) {
621 data = writeback_data_new (controller,
622 writeback_handlers,
623 priv->connection,
624 subject,
625 results,
626 invocation,
627 request);
628
629 g_io_scheduler_push_job (io_writeback_job, data, NULL, 0,
'g_io_scheduler_push_job' is deprecated (declared at /usr/include/glib-2.0/gio/gioscheduler.h:36): Use '"GThreadPool or g_task_run_in_thread"' instead
(emitted by gcc)
630 data->cancellable);
631 } else {
632 g_dbus_method_invocation_return_error (invocation,
633 TRACKER_DBUS_ERROR,
634 TRACKER_DBUS_ERROR_UNSUPPORTED,
635 "No module for rdf types");
636 }
637
638 g_free (rdf_types);
639 }
640
641 static void
642 handle_method_call_cancel_tasks (TrackerController *controller,
643 GDBusMethodInvocation *invocation,
644 GVariant *parameters)
645 {
646 TrackerDBusRequest *request;
647 gchar **subjects;
648 gint i;
649
650 #ifdef THREAD_ENABLE_TRACE
651 g_debug ("Thread:%p (Controller) --> Got Tasks cancellation request",
652 g_thread_self ());
653 #endif /* THREAD_ENABLE_TRACE */
654
655
656 g_variant_get (parameters, "(^as)", &subjects);
657
658 request = tracker_dbus_request_begin (NULL, "%s (%s, ...)", __FUNCTION__, subjects[0]);
659
660 for (i = 0; subjects[i] != NULL; i++) {
661 cancel_tasks (controller, subjects[i], NULL);
662 }
663
664 g_strfreev (subjects);
665 tracker_dbus_request_end (request, NULL);
666 g_dbus_method_invocation_return_value (invocation, NULL);
667 }
668
669 static void
670 handle_method_call (GDBusConnection *connection,
671 const gchar *sender,
672 const gchar *object_path,
673 const gchar *interface_name,
674 const gchar *method_name,
675 GVariant *parameters,
676 GDBusMethodInvocation *invocation,
677 gpointer user_data)
678 {
679 TrackerController *controller = user_data;
680
681 if (g_strcmp0 (method_name, "GetPid") == 0) {
682 handle_method_call_get_pid (controller, invocation, parameters);
683 } else if (g_strcmp0 (method_name, "PerformWriteback") == 0) {
684 handle_method_call_perform_writeback (controller, invocation, parameters);
685 } else if (g_strcmp0 (method_name, "CancelTasks") == 0) {
686 handle_method_call_cancel_tasks (controller, invocation, parameters);
687 } else {
688 g_warning ("Unknown method '%s' called", method_name);
689 }
690 }
691
692 static void
693 controller_notify_main_thread (TrackerController *controller,
694 GError *error)
695 {
696 TrackerControllerPrivate *priv;
697
698 priv = controller->priv;
699
700 #if GLIB_CHECK_VERSION (2,31,0)
701 g_mutex_lock (&priv->initialization_mutex);
702 #else
703 g_mutex_lock (priv->initialization_mutex);
704 #endif
705
706 priv->initialized = TRUE;
707 priv->initialization_error = error;
708
709 /* Notify about the initialization */
710 #if GLIB_CHECK_VERSION (2,31,0)
711 g_cond_signal (&priv->initialization_cond);
712 g_mutex_unlock (&priv->initialization_mutex);
713 #else
714 g_cond_signal (priv->initialization_cond);
715 g_mutex_unlock (priv->initialization_mutex);
716 #endif
717 }
718
719 static void
720 bus_name_acquired_cb (GDBusConnection *connection,
721 const gchar *name,
722 gpointer user_data)
723 {
724 controller_notify_main_thread (TRACKER_CONTROLLER (user_data), NULL);
725 }
726
727 static void
728 bus_name_vanished_cb (GDBusConnection *connection,
729 const gchar *name,
730 gpointer user_data)
731 {
732 TrackerController *controller;
733 TrackerControllerPrivate *priv;
734
735 controller = user_data;
736 priv = controller->priv;
737
738 if (!priv->initialized) {
739 GError *error;
740
741 error = g_error_new_literal (TRACKER_DBUS_ERROR, 0,
742 "Could not acquire bus name, "
743 "perhaps it's already taken?");
744 controller_notify_main_thread (controller, error);
745 } else {
746 /* We're already in control of the program
747 * lifetime, so just quit the mainloop
748 */
749 g_main_loop_quit (priv->main_loop);
750 }
751 }
752
753 static gboolean
754 tracker_controller_dbus_start (TrackerController *controller,
755 GError **error)
756 {
757 TrackerControllerPrivate *priv;
758 GError *err = NULL;
759 GDBusInterfaceVTable interface_vtable = {
760 handle_method_call,
761 NULL, NULL
762 };
763
764 priv = controller->priv;
765
766 priv->connection = tracker_sparql_connection_get (NULL, &err);
767
768 if (!priv->connection) {
769 g_propagate_error (error, err);
770 return FALSE;
771 }
772
773 priv->d_connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &err);
774
775 if (!priv->d_connection) {
776 g_propagate_error (error, err);
777 return FALSE;
778 }
779
780 priv->introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, &err);
781 if (!priv->introspection_data) {
782 g_propagate_error (error, err);
783 return FALSE;
784 }
785
786 g_message ("Registering D-Bus object...");
787 g_message (" Path:'" TRACKER_WRITEBACK_PATH "'");
788 g_message (" Object Type:'%s'", G_OBJECT_TYPE_NAME (controller));
789
790 priv->registration_id =
791 g_dbus_connection_register_object (priv->d_connection,
792 TRACKER_WRITEBACK_PATH,
793 priv->introspection_data->interfaces[0],
794 &interface_vtable,
795 controller,
796 NULL,
797 &err);
798
799 if (err) {
800 g_critical ("Could not register the D-Bus object "TRACKER_WRITEBACK_PATH", %s",
801 err ? err->message : "no error given.");
802 g_propagate_error (error, err);
803 return FALSE;
804 }
805
806 priv->bus_name_id =
807 g_bus_own_name_on_connection (priv->d_connection,
808 TRACKER_WRITEBACK_SERVICE,
809 G_BUS_NAME_OWNER_FLAGS_NONE,
810 bus_name_acquired_cb,
811 bus_name_vanished_cb,
812 controller, NULL);
813
814 if (err) {
815 g_critical ("Could not own the D-Bus name "TRACKER_WRITEBACK_SERVICE", %s",
816 err ? err->message : "no error given.");
817 g_propagate_error (error, err);
818 return FALSE;
819 }
820
821 return TRUE;
822 }
823
824 static void
825 tracker_controller_dbus_stop (TrackerController *controller)
826 {
827 TrackerControllerPrivate *priv;
828
829 priv = controller->priv;
830
831 if (priv->registration_id != 0) {
832 g_dbus_connection_unregister_object (priv->d_connection,
833 priv->registration_id);
834 }
835
836 if (priv->bus_name_id != 0) {
837 g_bus_unown_name (priv->bus_name_id);
838 }
839
840 if (priv->introspection_data) {
841 g_dbus_node_info_unref (priv->introspection_data);
842 }
843
844 if (priv->d_connection) {
845 g_object_unref (priv->d_connection);
846 }
847
848 if (priv->connection) {
849 g_object_unref (priv->connection);
850 }
851 }
852
853 TrackerController *
854 tracker_controller_new (guint shutdown_timeout,
855 GError **error)
856 {
857 return g_initable_new (TRACKER_TYPE_CONTROLLER,
858 NULL, error,
859 "shutdown-timeout", shutdown_timeout,
860 NULL);
861 }
862
863 static gpointer
864 tracker_controller_thread_func (gpointer user_data)
865 {
866 TrackerController *controller;
867 TrackerControllerPrivate *priv;
868 GError *error = NULL;
869
870 #ifdef THREAD_ENABLE_TRACE
871 g_debug ("Thread:%p (Controller) --- Created, dispatching...",
872 g_thread_self ());
873 #endif /* THREAD_ENABLE_TRACE */
874
875 controller = user_data;
876 priv = controller->priv;
877 g_main_context_push_thread_default (priv->context);
878
879 reset_shutdown_timeout (controller);
880
881 if (!tracker_controller_dbus_start (controller, &error)) {
882 /* Error has been filled in, so we return
883 * in this thread. The main thread will be
884 * notified about the error and exit.
885 */
886 controller_notify_main_thread (controller, error);
887 return NULL;
888 }
889
890 g_main_loop_run (priv->main_loop);
891
892 #ifdef THREAD_ENABLE_TRACE
893 g_debug ("Thread:%p (Controller) --- Shutting down...",
894 g_thread_self ());
895 #endif /* THREAD_ENABLE_TRACE */
896
897 g_object_unref (controller);
898
899 /* This is where we exit, be it
900 * either through umount events on monitored
901 * files' volumes or the timeout being reached
902 */
903 exit (0);
904 return NULL;
905 }
906
907 static gboolean
908 tracker_controller_start (TrackerController *controller,
909 GError **error)
910 {
911 TrackerControllerPrivate *priv;
912 GList *modules;
913
914 priv = controller->priv;
915
916 priv->modules = g_hash_table_new_full (g_str_hash,
917 g_str_equal,
918 (GDestroyNotify) g_free,
919 NULL);
920
921 modules = tracker_writeback_modules_list ();
922
923 while (modules) {
924 TrackerWritebackModule *module;
925 const gchar *path;
926
927 path = modules->data;
928 module = tracker_writeback_module_get (path);
929
930 if (module) {
931 g_hash_table_insert (priv->modules, g_strdup (path), module);
932 }
933
934 modules = modules->next;
935 }
936
937 #if GLIB_CHECK_VERSION (2,31,0)
938 {
939 GThread *thread;
940
941 thread = g_thread_try_new ("controller",
942 tracker_controller_thread_func,
943 controller,
944 error);
945 if (!thread)
946 return FALSE;
947
948 /* We don't want to join it, so just unref the GThread */
949 g_thread_unref (thread);
950 }
951 #else
952 if (!g_thread_create (tracker_controller_thread_func,
953 controller, FALSE, error)) {
954 return FALSE;
955 }
956 #endif
957
958
959 #ifdef THREAD_ENABLE_TRACE
960 g_debug ("Thread:%p (Controller) --- Waiting for controller thread to initialize...",
961 g_thread_self ());
962 #endif /* THREAD_ENABLE_TRACE */
963
964 /* Wait for the controller thread to notify initialization */
965 #if GLIB_CHECK_VERSION (2,31,0)
966 g_mutex_lock (&priv->initialization_mutex);
967 while (!priv->initialized)
968 g_cond_wait (&priv->initialization_cond, &priv->initialization_mutex);
969 g_mutex_unlock (&priv->initialization_mutex);
970 #else
971 g_mutex_lock (priv->initialization_mutex);
972 while (!priv->initialized)
973 g_cond_wait (priv->initialization_cond, priv->initialization_mutex);
974 g_mutex_unlock (priv->initialization_mutex);
975 #endif
976
977 /* If there was any error resulting from initialization, propagate it */
978 if (priv->initialization_error != NULL) {
979 g_propagate_error (error, priv->initialization_error);
980 return FALSE;
981 }
982
983 #ifdef THREAD_ENABLE_TRACE
984 g_debug ("Thread:%p (Controller) --- Initialized",
985 g_thread_self ());
986 #endif /* THREAD_ENABLE_TRACE */
987
988 return TRUE;
989 }