1 /*
2 * Copyright (C) 2010, 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 "config-miners.h"
21
22 #include <libtracker-miners-common/tracker-dbus.h>
23 #include <libtracker-sparql/tracker-sparql.h>
24
25 #include "tracker-writeback-dispatcher.h"
26 #include "tracker-miner-files.h"
27
28 #define TRACKER_WRITEBACK_SERVICE "org.freedesktop.Tracker1.Writeback"
29 #define TRACKER_WRITEBACK_PATH "/org/freedesktop/Tracker1/Writeback"
30 #define TRACKER_WRITEBACK_INTERFACE "org.freedesktop.Tracker1.Writeback"
31
32 typedef struct {
33 GFile *file;
34 TrackerMinerFiles *fs;
35 GPtrArray *results;
36 GStrv rdf_types;
37 TrackerWritebackDispatcher *self; /* weak */
38 GCancellable *cancellable;
39 guint cancel_id;
40 guint retry_timeout;
41 guint retries;
42 } WritebackFileData;
43
44 typedef struct {
45 TrackerMinerFiles *files_miner;
46 GDBusConnection *d_connection;
47 TrackerSparqlConnection *connection;
48 } TrackerWritebackDispatcherPrivate;
49
50 enum {
51 PROP_0,
52 PROP_FILES_MINER
53 };
54
55 #define TRACKER_WRITEBACK_DISPATCHER_GET_PRIVATE(o) (tracker_writeback_dispatcher_get_instance_private (TRACKER_WRITEBACK_DISPATCHER (o)))
56
57 static void writeback_dispatcher_set_property (GObject *object,
58 guint param_id,
59 const GValue *value,
60 GParamSpec *pspec);
61 static void writeback_dispatcher_get_property (GObject *object,
62 guint param_id,
63 GValue *value,
64 GParamSpec *pspec);
65 static void writeback_dispatcher_finalize (GObject *object);
66 static gboolean writeback_dispatcher_initable_init (GInitable *initable,
67 GCancellable *cancellable,
68 GError **error);
69 static void writeback_dispatcher_writeback_file (TrackerMinerFS *fs,
70 GFile *file,
71 GStrv rdf_types,
72 GPtrArray *results,
73 GCancellable *cancellable,
74 gpointer user_data);
75 static void self_weak_notify (gpointer data,
76 GObject *where_the_object_was);
77
78 static void
writeback_dispatcher_initable_iface_init(GInitableIface * iface)79 writeback_dispatcher_initable_iface_init (GInitableIface *iface)
80 {
81 iface->init = writeback_dispatcher_initable_init;
82 }
83
84 G_DEFINE_TYPE_WITH_CODE (TrackerWritebackDispatcher, tracker_writeback_dispatcher, G_TYPE_OBJECT,
85 G_ADD_PRIVATE (TrackerWritebackDispatcher)
86 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
87 writeback_dispatcher_initable_iface_init));
88
89 static void
tracker_writeback_dispatcher_class_init(TrackerWritebackDispatcherClass * klass)90 tracker_writeback_dispatcher_class_init (TrackerWritebackDispatcherClass *klass)
91 {
92 GObjectClass *object_class;
93
94 object_class = G_OBJECT_CLASS (klass);
95
96 object_class->finalize = writeback_dispatcher_finalize;
97 object_class->set_property = writeback_dispatcher_set_property;
98 object_class->get_property = writeback_dispatcher_get_property;
99
100 g_object_class_install_property (object_class,
101 PROP_FILES_MINER,
102 g_param_spec_object ("files_miner",
103 "files_miner",
104 "The FS Miner",
105 TRACKER_TYPE_MINER_FILES,
106 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
107 }
108
109 static void
writeback_dispatcher_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)110 writeback_dispatcher_set_property (GObject *object,
111 guint param_id,
112 const GValue *value,
113 GParamSpec *pspec)
114 {
115 TrackerWritebackDispatcherPrivate *priv;
116
117 priv = TRACKER_WRITEBACK_DISPATCHER_GET_PRIVATE (object);
118
119 switch (param_id) {
120 case PROP_FILES_MINER:
121 priv->files_miner = g_value_dup_object (value);
122 break;
123 default:
124 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
125 break;
126 }
127 }
128
129
130 static void
writeback_dispatcher_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)131 writeback_dispatcher_get_property (GObject *object,
132 guint param_id,
133 GValue *value,
134 GParamSpec *pspec)
135 {
136 TrackerWritebackDispatcherPrivate *priv;
137
138 priv = TRACKER_WRITEBACK_DISPATCHER_GET_PRIVATE (object);
139
140 switch (param_id) {
141 case PROP_FILES_MINER:
142 g_value_set_object (value, priv->files_miner);
143 break;
144 default:
145 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
146 break;
147 }
148 }
149
150 static void
writeback_dispatcher_finalize(GObject * object)151 writeback_dispatcher_finalize (GObject *object)
152 {
153 TrackerWritebackDispatcherPrivate *priv = TRACKER_WRITEBACK_DISPATCHER_GET_PRIVATE (object);
154
155 if (priv->connection) {
156 g_object_unref (priv->connection);
157 }
158
159 if (priv->d_connection) {
160 g_object_unref (priv->d_connection);
161 }
162
163 if (priv->files_miner) {
164 g_object_unref (priv->files_miner);
165 }
166 }
167
168 static void
tracker_writeback_dispatcher_init(TrackerWritebackDispatcher * object)169 tracker_writeback_dispatcher_init (TrackerWritebackDispatcher *object)
170 {
171 }
172
173 static gboolean
writeback_dispatcher_initable_init(GInitable * initable,GCancellable * cancellable,GError ** error)174 writeback_dispatcher_initable_init (GInitable *initable,
175 GCancellable *cancellable,
176 GError **error)
177 {
178 TrackerWritebackDispatcherPrivate *priv;
179 GError *internal_error = NULL;
180
181 priv = TRACKER_WRITEBACK_DISPATCHER_GET_PRIVATE (initable);
182
183 priv->connection = tracker_sparql_connection_get (NULL, &internal_error);
184
185 if (internal_error) {
186 g_propagate_error (error, internal_error);
187 return FALSE;
188 }
189
190 priv->d_connection = g_bus_get_sync (TRACKER_IPC_BUS, NULL, &internal_error);
191
192 if (internal_error) {
193 g_propagate_error (error, internal_error);
194 return FALSE;
195 }
196
197 g_signal_connect_object (priv->files_miner,
198 "writeback",
199 G_CALLBACK (writeback_dispatcher_writeback_file),
200 initable,
201 G_CONNECT_AFTER);
202
203 return TRUE;
204 }
205
206 TrackerWritebackDispatcher *
tracker_writeback_dispatcher_new(TrackerMinerFiles * miner_files,GError ** error)207 tracker_writeback_dispatcher_new (TrackerMinerFiles *miner_files,
208 GError **error)
209 {
210 GObject *miner;
211 GError *internal_error = NULL;
212
213 miner = g_initable_new (TRACKER_TYPE_WRITEBACK_DISPATCHER,
214 NULL,
215 &internal_error,
216 "files-miner", miner_files,
217 NULL);
218
219 if (internal_error) {
220 g_propagate_error (error, internal_error);
221 return NULL;
222 }
223
224 return (TrackerWritebackDispatcher *) miner;
225 }
226
227 static void
writeback_file_data_free(WritebackFileData * data)228 writeback_file_data_free (WritebackFileData *data)
229 {
230 if (data->self) {
231 g_object_weak_unref (G_OBJECT (data->self), self_weak_notify, data);
232 }
233
234 g_object_unref (data->fs);
235 g_object_unref (data->file);
236 g_strfreev (data->rdf_types);
237 g_ptr_array_unref (data->results);
238 g_cancellable_disconnect (data->cancellable, data->cancel_id);
239 g_object_unref (data->cancellable);
240 g_free (data);
241 }
242
243 static void
self_weak_notify(gpointer data,GObject * where_the_object_was)244 self_weak_notify (gpointer data, GObject *where_the_object_was)
245 {
246 WritebackFileData *udata = data;
247
248 /* Shut down while retrying writeback */
249 g_debug ("Shutdown occurred while retrying write-back (after unmount), not retrying anymore");
250
251 if (udata->retry_timeout != 0) {
252 g_source_remove (udata->retry_timeout);
253 }
254
255 udata->self = NULL;
256 writeback_file_data_free (udata);
257 }
258
259 static gboolean
retry_idle(gpointer user_data)260 retry_idle (gpointer user_data)
261 {
262 WritebackFileData *data = user_data;
263
264 tracker_miner_files_writeback_file (data->fs,
265 data->file,
266 data->rdf_types,
267 data->results);
268
269 writeback_file_data_free (data);
270
271 return FALSE;
272 }
273
274 static void
writeback_file_finished(GObject * source_object,GAsyncResult * res,gpointer user_data)275 writeback_file_finished (GObject *source_object,
276 GAsyncResult *res,
277 gpointer user_data)
278 {
279 WritebackFileData *data = user_data;
280 GError *error = NULL;
281
282 g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
283 res, &error);
284
285 if (error && error->code == G_DBUS_ERROR_NO_REPLY && data->retries < 5) {
286 /* This happens in case of exit() of the tracker-writeback binary, which
287 * happens on unmount of the FS event, for example */
288
289 g_debug ("Retrying write-back after unmount (timeout in 5 seconds)");
290 tracker_miner_files_writeback_notify (data->fs, data->file, NULL);
291
292 data->retry_timeout = g_timeout_add_seconds (5, retry_idle, data);
293 data->retries++;
294
295 } else {
296 tracker_miner_files_writeback_notify (data->fs, data->file, error);
297 writeback_file_data_free (data);
298 }
299 }
300
301 static void
writeback_cancel_remote_operation(GCancellable * cancellable,WritebackFileData * data)302 writeback_cancel_remote_operation (GCancellable *cancellable,
303 WritebackFileData *data)
304 {
305 TrackerWritebackDispatcherPrivate *priv;
306 GDBusMessage *message;
307 gchar *uris[2];
308
309 priv = TRACKER_WRITEBACK_DISPATCHER_GET_PRIVATE (data->self);
310
311 uris[0] = g_file_get_uri (data->file);
312 uris[1] = NULL;
313
314 message = g_dbus_message_new_method_call (TRACKER_WRITEBACK_SERVICE,
315 TRACKER_WRITEBACK_PATH,
316 TRACKER_WRITEBACK_INTERFACE,
317 "CancelTasks");
318
319 g_dbus_message_set_body (message, g_variant_new ("(^as)", uris));
320 g_dbus_connection_send_message (priv->d_connection, message,
321 G_DBUS_SEND_MESSAGE_FLAGS_NONE,
322 NULL, NULL);
323 g_free (uris[0]);
324 }
325
326 static void
writeback_dispatcher_writeback_file(TrackerMinerFS * fs,GFile * file,GStrv rdf_types,GPtrArray * results,GCancellable * cancellable,gpointer user_data)327 writeback_dispatcher_writeback_file (TrackerMinerFS *fs,
328 GFile *file,
329 GStrv rdf_types,
330 GPtrArray *results,
331 GCancellable *cancellable,
332 gpointer user_data)
333 {
334 TrackerWritebackDispatcher *self = user_data;
335 TrackerWritebackDispatcherPrivate *priv;
336 gchar *uri;
337 guint i;
338 GVariantBuilder builder;
339 WritebackFileData *data = g_new (WritebackFileData, 1);
340
341 priv = TRACKER_WRITEBACK_DISPATCHER_GET_PRIVATE (self);
342
343 uri = g_file_get_uri (file);
344 g_debug ("Performing write-back for '%s'", uri);
345
346 g_variant_builder_init (&builder, G_VARIANT_TYPE ("(sasaas)"));
347
348 g_variant_builder_add (&builder, "s", uri);
349
350 g_variant_builder_open (&builder, G_VARIANT_TYPE ("as"));
351 for (i = 0; rdf_types[i] != NULL; i++) {
352 g_variant_builder_add (&builder, "s", rdf_types[i]);
353 }
354 g_variant_builder_close (&builder);
355
356 g_variant_builder_open (&builder, G_VARIANT_TYPE ("aas"));
357
358 for (i = 0; i< results->len; i++) {
359 GStrv row = g_ptr_array_index (results, i);
360 guint y;
361
362 g_variant_builder_open (&builder, G_VARIANT_TYPE ("as"));
363 for (y = 0; row[y] != NULL; y++) {
364 g_variant_builder_add (&builder, "s", row[y]);
365 }
366
367 g_variant_builder_close (&builder);
368 }
369
370 g_variant_builder_close (&builder);
371
372 data->retries = 0;
373 data->retry_timeout = 0;
374 data->self = self;
375 g_object_weak_ref (G_OBJECT (data->self), self_weak_notify, data);
376 data->fs = TRACKER_MINER_FILES (g_object_ref (fs));
377 data->file = g_object_ref (file);
378 data->results = g_ptr_array_ref (results);
379 data->rdf_types = g_strdupv (rdf_types);
380 data->cancellable = g_object_ref (cancellable);
381 data->cancel_id = g_cancellable_connect (data->cancellable,
382 G_CALLBACK (writeback_cancel_remote_operation),
383 data, NULL);
384
385 g_dbus_connection_call (priv->d_connection,
386 TRACKER_WRITEBACK_SERVICE,
387 TRACKER_WRITEBACK_PATH,
388 TRACKER_WRITEBACK_INTERFACE,
389 "PerformWriteback",
390 g_variant_builder_end (&builder),
391 NULL,
392 G_DBUS_CALL_FLAGS_NONE,
393 -1,
394 cancellable,
395 (GAsyncReadyCallback) writeback_file_finished,
396 data);
397
398 g_free (uri);
399 }
400