tracker-0.16.2/src/miners/fs/tracker-writeback-dispatcher.c

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 }