17
17
* Boston, MA 02110-1301, USA.
22
20
#include "tracker-writeback.h"
24
G_DEFINE_ABSTRACT_TYPE (TrackerWriteback, tracker_writeback, G_TYPE_OBJECT)
26
static TrackerMinerManager *manager = NULL;
29
tracker_writeback_class_init (TrackerWritebackClass *klass)
34
tracker_writeback_init (TrackerWriteback *writeback)
39
tracker_writeback_update_metadata (TrackerWriteback *writeback,
41
TrackerSparqlConnection *connection)
43
g_return_val_if_fail (TRACKER_IS_WRITEBACK (writeback), FALSE);
44
g_return_val_if_fail (values != NULL, FALSE);
46
if (TRACKER_WRITEBACK_GET_CLASS (writeback)->update_metadata) {
47
return TRACKER_WRITEBACK_GET_CLASS (writeback)->update_metadata (writeback, values, connection);
54
tracker_writeback_get_miner_manager (void)
57
manager = tracker_miner_manager_new ();
21
#include "tracker-writeback-module.h"
23
#include <libtracker-common/tracker-common.h>
24
#include <libtracker-miner/tracker-miner.h>
25
#include <libtracker-sparql/tracker-sparql.h>
29
#ifdef STAYALIVE_ENABLE_TRACE
30
#warning Stayalive traces enabled
31
#endif /* STAYALIVE_ENABLE_TRACE */
33
#define THREAD_ENABLE_TRACE
35
#ifdef THREAD_ENABLE_TRACE
36
#warning Controller thread traces enabled
37
#endif /* THREAD_ENABLE_TRACE */
40
TrackerController *controller;
41
GCancellable *cancellable;
42
GDBusMethodInvocation *invocation;
43
TrackerDBusRequest *request;
46
TrackerSparqlConnection *connection;
47
TrackerWriteback *writeback;
52
GMainContext *context;
55
TrackerStorage *storage;
57
GDBusConnection *d_connection;
58
GDBusNodeInfo *introspection_data;
59
guint registration_id;
64
guint shutdown_timeout;
65
GSource *shutdown_source;
67
GCond *initialization_cond;
68
GMutex *initialization_mutex, *mutex;
69
GError *initialization_error;
71
guint initialized : 1;
74
TrackerSparqlConnection *connection;
75
WritebackData *current;
76
} TrackerControllerPrivate;
78
#define TRACKER_WRITEBACK_SERVICE "org.freedesktop.Tracker1.Writeback"
79
#define TRACKER_WRITEBACK_PATH "/org/freedesktop/Tracker1/Writeback"
80
#define TRACKER_WRITEBACK_INTERFACE "org.freedesktop.Tracker1.Writeback"
82
static const gchar *introspection_xml =
84
" <interface name='org.freedesktop.Tracker1.Writeback'>"
85
" <method name='GetPid'>"
86
" <arg type='i' name='value' direction='out' />"
88
" <method name='PerformWriteback'>"
89
" <arg type='s' name='subject' direction='in' />"
90
" <arg type='as' name='rdf_types' direction='in' />"
91
" <arg type='aas' name='results' direction='in' />"
93
" <method name='CancelTasks'>"
94
" <arg type='as' name='subjects' direction='in' />"
101
PROP_SHUTDOWN_TIMEOUT,
104
static void tracker_controller_initable_iface_init (GInitableIface *iface);
105
static gboolean tracker_controller_dbus_start (TrackerController *controller,
107
static void tracker_controller_dbus_stop (TrackerController *controller);
108
static gboolean tracker_controller_start (TrackerController *controller,
111
G_DEFINE_TYPE_WITH_CODE (TrackerController, tracker_controller, G_TYPE_OBJECT,
112
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
113
tracker_controller_initable_iface_init));
116
tracker_controller_initable_init (GInitable *initable,
117
GCancellable *cancellable,
120
return tracker_controller_start (TRACKER_CONTROLLER (initable), error);
124
tracker_controller_initable_iface_init (GInitableIface *iface)
126
iface->init = tracker_controller_initable_init;
130
tracker_controller_finalize (GObject *object)
132
TrackerControllerPrivate *priv;
133
TrackerController *controller;
135
controller = TRACKER_CONTROLLER (object);
136
priv = controller->priv;
138
if (priv->shutdown_source) {
139
g_source_destroy (priv->shutdown_source);
140
priv->shutdown_source = NULL;
143
tracker_controller_dbus_stop (controller);
145
g_object_unref (priv->storage);
146
g_hash_table_unref (priv->modules);
148
g_main_loop_unref (priv->main_loop);
149
g_main_context_unref (priv->context);
151
g_cond_free (priv->initialization_cond);
152
g_mutex_free (priv->initialization_mutex);
153
g_mutex_free (priv->mutex);
155
G_OBJECT_CLASS (tracker_controller_parent_class)->finalize (object);
159
tracker_controller_get_property (GObject *object,
164
TrackerControllerPrivate *priv = TRACKER_CONTROLLER (object)->priv;
167
case PROP_SHUTDOWN_TIMEOUT:
168
g_value_set_uint (value, priv->shutdown_timeout);
174
tracker_controller_set_property (GObject *object,
179
TrackerControllerPrivate *priv = TRACKER_CONTROLLER (object)->priv;
182
case PROP_SHUTDOWN_TIMEOUT:
183
priv->shutdown_timeout = g_value_get_uint (value);
189
tracker_controller_class_init (TrackerControllerClass *klass)
191
GObjectClass *object_class = G_OBJECT_CLASS (klass);
193
object_class->finalize = tracker_controller_finalize;
194
object_class->get_property = tracker_controller_get_property;
195
object_class->set_property = tracker_controller_set_property;
197
g_object_class_install_property (object_class,
198
PROP_SHUTDOWN_TIMEOUT,
199
g_param_spec_uint ("shutdown-timeout",
201
"Shutdown timeout, 0 to disable",
203
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
205
g_type_class_add_private (object_class, sizeof (TrackerControllerPrivate));
209
task_cancellable_cancelled_cb (GCancellable *cancellable,
212
TrackerControllerPrivate *priv;
214
priv = data->controller->priv;
216
g_mutex_lock (priv->mutex);
218
if (priv->current == data) {
219
g_message ("Cancelled writeback task for '%s' was currently being "
220
"processed, _exit()ing immediately",
225
g_mutex_unlock (priv->mutex);
229
static WritebackData *
230
writeback_data_new (TrackerController *controller,
231
TrackerWriteback *writeback,
232
TrackerSparqlConnection *connection,
233
const gchar *subject,
235
GDBusMethodInvocation *invocation,
236
TrackerDBusRequest *request)
240
data = g_slice_new (WritebackData);
241
data->cancellable = g_cancellable_new ();
242
data->controller = g_object_ref (controller);
243
data->subject = g_strdup (subject);
244
data->results = g_ptr_array_ref (results);
245
data->invocation = invocation;
246
data->connection = g_object_ref (connection);
247
data->writeback = g_object_ref (writeback);
248
data->request = request;
250
data->cancel_id = g_cancellable_connect (data->cancellable,
251
G_CALLBACK (task_cancellable_cancelled_cb),
258
writeback_data_free (WritebackData *data)
260
/* We rely on data->invocation being freed through
261
* the g_dbus_method_invocation_return_* methods
263
g_cancellable_disconnect (data->cancellable, data->cancel_id);
264
g_free (data->subject);
265
g_object_unref (data->connection);
266
g_object_unref (data->writeback);
267
g_ptr_array_unref (data->results);
268
g_object_unref (data->cancellable);
269
g_slice_free (WritebackData, data);
273
cancel_tasks (TrackerController *controller,
274
const gchar *subject,
277
TrackerControllerPrivate *priv;
280
priv = controller->priv;
282
for (elem = priv->ongoing_tasks; elem; elem = elem->next) {
283
WritebackData *data = elem->data;
285
if (g_strcmp0 (subject, data->subject) == 0) {
286
g_message ("Cancelling not yet processed task ('%s')",
288
g_cancellable_cancel (data->cancellable);
293
for (i = 0; i < data->results->len; i++) {
294
GStrv row = g_ptr_array_index (data->results, i);
295
if (row[0] != NULL) {
297
task_file = g_file_new_for_uri (row[0]);
298
if (g_file_equal (task_file, file) ||
299
g_file_has_prefix (task_file, file)) {
300
/* Mount path contains some file being processed */
301
g_message ("Cancelling task ('%s')", row[0]);
302
g_cancellable_cancel (data->cancellable);
304
g_object_unref (task_file);
312
mount_point_removed_cb (TrackerStorage *storage,
314
const gchar *mount_point,
319
mount_file = g_file_new_for_path (mount_point);
320
cancel_tasks (TRACKER_CONTROLLER (user_data), NULL, mount_file);
321
g_object_unref (mount_file);
325
reset_shutdown_timeout_cb (gpointer user_data)
327
TrackerControllerPrivate *priv;
329
#ifdef STAYALIVE_ENABLE_TRACE
330
g_debug ("Stayalive --- time has expired");
331
#endif /* STAYALIVE_ENABLE_TRACE */
333
g_message ("Shutting down due to no activity");
335
priv = TRACKER_CONTROLLER (user_data)->priv;
336
g_main_loop_quit (priv->main_loop);
342
reset_shutdown_timeout (TrackerController *controller)
344
TrackerControllerPrivate *priv;
347
priv = controller->priv;
349
if (priv->shutdown_timeout == 0) {
353
#ifdef STAYALIVE_ENABLE_TRACE
354
g_debug ("Stayalive --- (Re)setting timeout");
355
#endif /* STAYALIVE_ENABLE_TRACE */
357
if (priv->shutdown_source) {
358
g_source_destroy (priv->shutdown_source);
359
priv->shutdown_source = NULL;
362
source = g_timeout_source_new_seconds (priv->shutdown_timeout);
363
g_source_set_callback (source,
364
reset_shutdown_timeout_cb,
367
g_source_attach (source, priv->context);
368
priv->shutdown_source = source;
372
tracker_controller_init (TrackerController *controller)
374
TrackerControllerPrivate *priv;
376
priv = controller->priv = G_TYPE_INSTANCE_GET_PRIVATE (controller,
377
TRACKER_TYPE_CONTROLLER,
378
TrackerControllerPrivate);
380
priv->context = g_main_context_new ();
381
priv->main_loop = g_main_loop_new (priv->context, FALSE);
383
priv->storage = tracker_storage_new ();
384
g_signal_connect (priv->storage, "mount-point-removed",
385
G_CALLBACK (mount_point_removed_cb), controller);
387
priv->initialization_cond = g_cond_new ();
388
priv->initialization_mutex = g_mutex_new ();
389
priv->mutex = g_mutex_new ();
393
handle_method_call_get_pid (TrackerController *controller,
394
GDBusMethodInvocation *invocation,
395
GVariant *parameters)
397
TrackerDBusRequest *request;
400
request = tracker_g_dbus_request_begin (invocation,
404
reset_shutdown_timeout (controller);
406
tracker_dbus_request_debug (request,
410
tracker_dbus_request_end (request, NULL);
412
g_dbus_method_invocation_return_value (invocation,
413
g_variant_new ("(i)", (gint) value));
417
perform_writeback_cb (gpointer user_data)
419
TrackerControllerPrivate *priv;
423
priv = data->controller->priv;
424
priv->ongoing_tasks = g_list_remove (priv->ongoing_tasks, data);
425
g_dbus_method_invocation_return_value (data->invocation, NULL);
426
tracker_dbus_request_end (data->request, NULL);
428
g_mutex_lock (priv->mutex);
429
priv->current = NULL;
430
g_mutex_unlock (priv->mutex);
432
writeback_data_free (data);
438
sparql_rdf_types_match (const gchar * const *module_types,
439
const gchar * const *rdf_types)
443
for (n = 0; rdf_types[n] != NULL; n++) {
446
for (i = 0; module_types[i] != NULL; i++) {
447
if (g_strcmp0 (module_types[i], rdf_types[n]) == 0) {
457
io_writeback_job (GIOSchedulerJob *job,
458
GCancellable *cancellable,
461
WritebackData *data = user_data;
462
TrackerControllerPrivate *priv = data->controller->priv;
464
g_mutex_lock (priv->mutex);
465
priv->current = data;
466
g_mutex_unlock (priv->mutex);
468
tracker_writeback_update_metadata (data->writeback,
473
g_idle_add (perform_writeback_cb, data);
479
handle_method_call_perform_writeback (TrackerController *controller,
480
GDBusMethodInvocation *invocation,
481
GVariant *parameters)
483
TrackerControllerPrivate *priv;
484
TrackerDBusRequest *request;
485
const gchar *subject;
486
GPtrArray *results = NULL;
489
GVariantIter *iter1, *iter2, *iter3;
490
GArray *rdf_types_array;
492
gchar *rdf_type = NULL;
494
priv = controller->priv;
496
results = g_ptr_array_new_with_free_func ((GDestroyNotify) g_strfreev);
497
g_variant_get (parameters, "(&sasaas)", &subject, &iter1, &iter2);
499
rdf_types_array = g_array_new (TRUE, TRUE, sizeof (gchar *));
500
while (g_variant_iter_loop (iter1, "&s", &rdf_type)) {
501
g_array_append_val (rdf_types_array, rdf_type);
504
rdf_types = (GStrv) rdf_types_array->data;
505
g_array_free (rdf_types_array, FALSE);
507
while (g_variant_iter_loop (iter2, "as", &iter3)) {
508
GArray *row_array = g_array_new (TRUE, TRUE, sizeof (gchar *));
511
while (g_variant_iter_loop (iter3, "&s", &cell)) {
512
g_array_append_val (row_array, cell);
515
g_ptr_array_add (results, row_array->data);
516
g_array_free (row_array, FALSE);
519
g_variant_iter_free (iter1);
520
g_variant_iter_free (iter2);
522
reset_shutdown_timeout (controller);
523
request = tracker_dbus_request_begin (NULL, "%s (%s)", __FUNCTION__, subject);
525
g_hash_table_iter_init (&iter, priv->modules);
527
while (g_hash_table_iter_next (&iter, &key, &value)) {
528
TrackerWritebackModule *module;
529
const gchar * const *module_types;
532
module_types = tracker_writeback_module_get_rdf_types (module);
534
if (sparql_rdf_types_match (module_types, (const gchar * const *) rdf_types)) {
536
TrackerWriteback *writeback;
538
g_message (" Updating metadata for subject:'%s' using module:'%s'",
542
writeback = tracker_writeback_module_create (module);
543
data = writeback_data_new (controller,
551
g_io_scheduler_push_job (io_writeback_job, data, NULL, 0,
554
g_object_unref (writeback);
562
handle_method_call_cancel_tasks (TrackerController *controller,
563
GDBusMethodInvocation *invocation,
564
GVariant *parameters)
566
TrackerDBusRequest *request;
570
#ifdef THREAD_ENABLE_TRACE
571
g_debug ("Thread:%p (Controller) --> Got Tasks cancellation request",
573
#endif /* THREAD_ENABLE_TRACE */
576
g_variant_get (parameters, "(^as)", &subjects);
578
request = tracker_dbus_request_begin (NULL, "%s (%s, ...)", __FUNCTION__, subjects[0]);
580
for (i = 0; subjects[i] != NULL; i++) {
581
cancel_tasks (controller, subjects[i], NULL);
584
g_strfreev (subjects);
585
tracker_dbus_request_end (request, NULL);
586
g_dbus_method_invocation_return_value (invocation, NULL);
590
handle_method_call (GDBusConnection *connection,
592
const gchar *object_path,
593
const gchar *interface_name,
594
const gchar *method_name,
595
GVariant *parameters,
596
GDBusMethodInvocation *invocation,
599
TrackerController *controller = user_data;
601
if (g_strcmp0 (method_name, "GetPid") == 0) {
602
handle_method_call_get_pid (controller, invocation, parameters);
603
} else if (g_strcmp0 (method_name, "PerformWriteback") == 0) {
604
handle_method_call_perform_writeback (controller, invocation, parameters);
605
} else if (g_strcmp0 (method_name, "CancelTasks") == 0) {
606
handle_method_call_cancel_tasks (controller, invocation, parameters);
608
g_warning ("Unknown method '%s' called", method_name);
613
controller_notify_main_thread (TrackerController *controller,
616
TrackerControllerPrivate *priv;
618
priv = controller->priv;
620
g_mutex_lock (priv->initialization_mutex);
622
priv->initialized = TRUE;
623
priv->initialization_error = error;
625
/* Notify about the initialization */
626
g_cond_signal (priv->initialization_cond);
628
g_mutex_unlock (priv->initialization_mutex);
632
bus_name_acquired_cb (GDBusConnection *connection,
636
controller_notify_main_thread (TRACKER_CONTROLLER (user_data), NULL);
640
bus_name_vanished_cb (GDBusConnection *connection,
644
TrackerController *controller;
645
TrackerControllerPrivate *priv;
647
controller = user_data;
648
priv = controller->priv;
650
if (!priv->initialized) {
653
error = g_error_new_literal (TRACKER_DBUS_ERROR, 0,
654
"Could not acquire bus name, "
655
"perhaps it's already taken?");
656
controller_notify_main_thread (controller, error);
658
/* We're already in control of the program
659
* lifetime, so just quit the mainloop
661
g_main_loop_quit (priv->main_loop);
666
tracker_controller_dbus_start (TrackerController *controller,
669
TrackerControllerPrivate *priv;
671
GDBusInterfaceVTable interface_vtable = {
676
priv = controller->priv;
678
priv->connection = tracker_sparql_connection_get (NULL, &err);
680
if (!priv->connection) {
681
g_propagate_error (error, err);
685
priv->d_connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &err);
687
if (!priv->d_connection) {
688
g_propagate_error (error, err);
692
priv->introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, &err);
693
if (!priv->introspection_data) {
694
g_propagate_error (error, err);
698
g_message ("Registering D-Bus object...");
699
g_message (" Path:'" TRACKER_WRITEBACK_PATH "'");
700
g_message (" Object Type:'%s'", G_OBJECT_TYPE_NAME (controller));
703
g_bus_own_name_on_connection (priv->d_connection,
704
TRACKER_WRITEBACK_SERVICE,
705
G_BUS_NAME_OWNER_FLAGS_NONE,
706
bus_name_acquired_cb,
707
bus_name_vanished_cb,
710
priv->registration_id =
711
g_dbus_connection_register_object (priv->d_connection,
712
TRACKER_WRITEBACK_PATH,
713
priv->introspection_data->interfaces[0],
720
g_propagate_error (error, err);
728
tracker_controller_dbus_stop (TrackerController *controller)
730
TrackerControllerPrivate *priv;
732
priv = controller->priv;
734
if (priv->registration_id != 0) {
735
g_dbus_connection_unregister_object (priv->d_connection,
736
priv->registration_id);
739
if (priv->bus_name_id != 0) {
740
g_bus_unown_name (priv->bus_name_id);
743
if (priv->introspection_data) {
744
g_dbus_node_info_unref (priv->introspection_data);
747
if (priv->d_connection) {
748
g_object_unref (priv->d_connection);
751
if (priv->connection) {
752
g_object_unref (priv->connection);
757
tracker_controller_new (guint shutdown_timeout,
760
return g_initable_new (TRACKER_TYPE_CONTROLLER,
762
"shutdown-timeout", shutdown_timeout,
767
tracker_controller_thread_func (gpointer user_data)
769
TrackerController *controller;
770
TrackerControllerPrivate *priv;
771
GError *error = NULL;
773
#ifdef THREAD_ENABLE_TRACE
774
g_debug ("Thread:%p (Controller) --- Created, dispatching...",
776
#endif /* THREAD_ENABLE_TRACE */
778
controller = user_data;
779
priv = controller->priv;
780
g_main_context_push_thread_default (priv->context);
782
reset_shutdown_timeout (controller);
784
if (!tracker_controller_dbus_start (controller, &error)) {
785
/* Error has been filled in, so we return
786
* in this thread. The main thread will be
787
* notified about the error and exit.
789
controller_notify_main_thread (controller, error);
793
g_main_loop_run (priv->main_loop);
795
#ifdef THREAD_ENABLE_TRACE
796
g_debug ("Thread:%p (Controller) --- Shutting down...",
798
#endif /* THREAD_ENABLE_TRACE */
800
g_object_unref (controller);
802
/* This is where we exit, be it
803
* either through umount events on monitored
804
* files' volumes or the timeout being reached
811
tracker_controller_start (TrackerController *controller,
814
TrackerControllerPrivate *priv;
817
priv = controller->priv;
819
priv->modules = g_hash_table_new_full (g_str_hash,
821
(GDestroyNotify) g_free,
824
modules = tracker_writeback_modules_list ();
827
TrackerWritebackModule *module;
830
path = modules->data;
831
module = tracker_writeback_module_get (path);
834
g_hash_table_insert (priv->modules, g_strdup (path), module);
837
modules = modules->next;
840
if (!g_thread_create (tracker_controller_thread_func,
841
controller, FALSE, error)) {
846
#ifdef THREAD_ENABLE_TRACE
847
g_debug ("Thread:%p (Controller) --- Waiting for controller thread to initialize...",
849
#endif /* THREAD_ENABLE_TRACE */
851
/* Wait for the controller thread to notify initialization */
852
g_mutex_lock (priv->initialization_mutex);
854
while (!priv->initialized) {
855
g_cond_wait (priv->initialization_cond, priv->initialization_mutex);
858
g_mutex_unlock (priv->initialization_mutex);
860
/* If there was any error resulting from initialization, propagate it */
861
if (priv->initialization_error != NULL) {
862
g_propagate_error (error, priv->initialization_error);
866
#ifdef THREAD_ENABLE_TRACE
867
g_debug ("Thread:%p (Controller) --- Initialized",
869
#endif /* THREAD_ENABLE_TRACE */