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