hythmbox-2.98/rhythmdb/rhythmdb-monitor.c

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found rhythmdb-monitor.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
clang-analyzer no-output-found rhythmdb-monitor.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
  1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  2  *
  3  *  Copyright (C) 2003,2004 Colin Walters <walters@gnome.org>
  4  *
  5  *  This program is free software; you can redistribute it and/or modify
  6  *  it under the terms of the GNU General Public License as published by
  7  *  the Free Software Foundation; either version 2 of the License, or
  8  *  (at your option) any later version.
  9  *
 10  *  The Rhythmbox authors hereby grant permission for non-GPL compatible
 11  *  GStreamer plugins to be used and distributed together with GStreamer
 12  *  and Rhythmbox. This permission is above and beyond the permissions granted
 13  *  by the GPL license by which Rhythmbox is covered. If you modify this code
 14  *  you may extend this exception to your version of the code, but you are not
 15  *  obligated to do so. If you do not wish to do so, delete this exception
 16  *  statement from your version.
 17  *
 18  *  This program is distributed in the hope that it will be useful,
 19  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 20  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 21  *  GNU General Public License for more details.
 22  *
 23  *  You should have received a copy of the GNU General Public License
 24  *  along with this program; if not, write to the Free Software
 25  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
 26  *
 27  */
 28 
 29 #include <config.h>
 30 
 31 #include <string.h>
 32 
 33 #include <glib.h>
 34 #include <glib-object.h>
 35 #include <glib/gi18n.h>
 36 #include <gio/gio.h>
 37 
 38 #include "rb-debug.h"
 39 #include "rb-util.h"
 40 #include "rhythmdb.h"
 41 #include "rhythmdb-private.h"
 42 #include "rhythmdb-query-result-list.h"
 43 #include "rb-file-helpers.h"
 44 
 45 #define RHYTHMDB_FILE_MODIFY_PROCESS_TIME 2
 46 
 47 static void rhythmdb_directory_change_cb (GFileMonitor *monitor,
 48 					  GFile *file,
 49 					  GFile *other_file,
 50 					  GFileMonitorEvent event_type,
 51 					  RhythmDB *db);
 52 static void rhythmdb_mount_added_cb (GVolumeMonitor *monitor,
 53 				     GMount *mount,
 54 				     RhythmDB *db);
 55 static void rhythmdb_mount_removed_cb (GVolumeMonitor *monitor,
 56 				       GMount *mount,
 57 				       RhythmDB *db);
 58 
 59 void
 60 rhythmdb_init_monitoring (RhythmDB *db)
 61 {
 62 	db->priv->monitored_directories = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal,
 63 								 (GDestroyNotify) g_object_unref,
 64 								 (GDestroyNotify)g_file_monitor_cancel);
 65 
 66 	db->priv->changed_files = g_hash_table_new_full (rb_refstring_hash, rb_refstring_equal,
 67 							 (GDestroyNotify) rb_refstring_unref,
 68 							 NULL);
 69 
 70 	db->priv->volume_monitor = g_volume_monitor_get ();
 71 	g_signal_connect (G_OBJECT (db->priv->volume_monitor),
 72 			  "mount-added",
 73 			  G_CALLBACK (rhythmdb_mount_added_cb),
 74 			  db);
 75 
 76 	g_signal_connect (G_OBJECT (db->priv->volume_monitor),
 77 			  "mount-removed",
 78 			  G_CALLBACK (rhythmdb_mount_removed_cb),
 79 			  db);
 80 	g_signal_connect (G_OBJECT (db->priv->volume_monitor),
 81 			  "mount-pre-unmount",
 82 			  G_CALLBACK (rhythmdb_mount_removed_cb),
 83 			  db);
 84 }
 85 
 86 void
 87 rhythmdb_dispose_monitoring (RhythmDB *db)
 88 {
 89 	if (db->priv->changed_files_id != 0) {
 90 		g_source_remove (db->priv->changed_files_id);
 91 		db->priv->changed_files_id = 0;
 92 	}
 93 
 94 	if (db->priv->volume_monitor != NULL) {
 95 		g_object_unref (db->priv->volume_monitor);
 96 		db->priv->volume_monitor = NULL;
 97 	}
 98 }
 99 
100 void
101 rhythmdb_finalize_monitoring (RhythmDB *db)
102 {
103 	rhythmdb_stop_monitoring (db);
104 
105 	g_hash_table_destroy (db->priv->monitored_directories);
106 	g_hash_table_destroy (db->priv->changed_files);
107 }
108 
109 void
110 rhythmdb_stop_monitoring (RhythmDB *db)
111 {
112 	g_hash_table_foreach_remove (db->priv->monitored_directories,
113 				     (GHRFunc) rb_true_function,
114 				     db);
115 }
116 
117 static void
118 actually_add_monitor (RhythmDB *db, GFile *directory, GError **error)
119 {
120 	GFileMonitor *monitor;
121 
122 	if (directory == NULL) {
123 		return;
124 	}
125 
126 	g_mutex_lock (&db->priv->monitor_mutex);
127 
128 	if (g_hash_table_lookup (db->priv->monitored_directories, directory)) {
129 		g_mutex_unlock (&db->priv->monitor_mutex);
130 		return;
131 	}
132 
133 	monitor = g_file_monitor_directory (directory, G_FILE_MONITOR_SEND_MOVED, db->priv->exiting, error);
134 	if (monitor != NULL) {
135 		g_signal_connect_object (G_OBJECT (monitor),
136 					 "changed",
137 					 G_CALLBACK (rhythmdb_directory_change_cb),
138 					 db, 0);
139 		g_hash_table_insert (db->priv->monitored_directories,
140 				     g_object_ref (directory),
141 				     monitor);
142 	}
143 
144 	g_mutex_unlock (&db->priv->monitor_mutex);
145 }
146 
147 static gboolean
148 monitor_subdirectory (GFile *file, gboolean dir, RhythmDB *db)
149 {
150 	char *uri;
151 
152 	uri = g_file_get_uri (file);
153 	if (dir) {
154 		actually_add_monitor (db, file, NULL);
155 	} else {
156 		/* add the file to the database if it's not already there */
157 		RhythmDBEntry *entry;
158 
159 		entry = rhythmdb_entry_lookup_by_location (db, uri);
160 		if (entry == NULL) {
161 			rhythmdb_add_uri (db, uri);
162 		}
163 	}
164 	g_free (uri);
165 	return TRUE;	
166 }
167 
168 static void
169 monitor_library_directory (const char *uri, RhythmDB *db)
170 {
171 	if ((strcmp (uri, "file:///") == 0) ||
172 	    (strcmp (uri, "file://") == 0)) {
173 		/* display an error to the user? */
174 		return;
175 	}
176 
177 	rb_debug ("beginning monitor of the library directory %s", uri);
178 	rhythmdb_monitor_uri_path (db, uri, NULL);
179 	rb_uri_handle_recursively_async (uri,
180 					 NULL,
181 					 (RBUriRecurseFunc) monitor_subdirectory,
182 					 g_object_ref (db),
183 					 (GDestroyNotify)g_object_unref);
184 }
185 
186 static gboolean
187 rhythmdb_check_changed_file (RBRefString *uri, gpointer data, RhythmDB *db)
188 {
189 	GTimeVal time;
190 	glong time_sec = GPOINTER_TO_INT (data);
191 
192 	g_get_current_time (&time);
193 	if (time.tv_sec >= time_sec + RHYTHMDB_FILE_MODIFY_PROCESS_TIME) {
194 		rb_debug ("adding newly located file %s", rb_refstring_get (uri));
195 		rhythmdb_add_uri (db, rb_refstring_get (uri));
196 		return TRUE;
197 	}
198 
199 	rb_debug ("waiting to add newly located file %s", rb_refstring_get (uri));
200 
201 	return FALSE;
202 }
203 
204 static gboolean
205 rhythmdb_process_changed_files (RhythmDB *db)
206 {
207 	/*
208 	 * no need for a mutex around the changed files map as it's only accessed
209 	 * from the main thread.  GFileMonitor's 'changed' signal is emitted from an
210 	 * idle handler, and we only process the map in a timeout callback.
211 	 */
212 	if (g_hash_table_size (db->priv->changed_files) == 0) {
213 		db->priv->changed_files_id = 0;
214 		return FALSE;
215 	}
216 
217 	g_hash_table_foreach_remove (db->priv->changed_files,
218 				     (GHRFunc)rhythmdb_check_changed_file, db);
219 	return TRUE;
220 }
221 
222 void
223 rhythmdb_start_monitoring (RhythmDB *db)
224 {
225 	/* monitor all library locations */
226 	if (db->priv->library_locations) {
227 		int i;
228 		for (i = 0; db->priv->library_locations[i] != NULL; i++) {
229 			monitor_library_directory (db->priv->library_locations[i], db);
230 		}
231 	}
232 }
233 
234 static void
235 add_changed_file (RhythmDB *db, const char *uri)
236 {
237 	GTimeVal time;
238 
239 	g_get_current_time (&time);
240 	g_hash_table_replace (db->priv->changed_files,
241 			      rb_refstring_new (uri),
242 			      GINT_TO_POINTER (time.tv_sec));
243 	if (db->priv->changed_files_id == 0) {
244 		db->priv->changed_files_id =
245 			g_timeout_add_seconds (RHYTHMDB_FILE_MODIFY_PROCESS_TIME,
246 					       (GSourceFunc) rhythmdb_process_changed_files,
247 					       db);
248 	}
249 }
250 
251 static void
252 rhythmdb_directory_change_cb (GFileMonitor *monitor,
253 			      GFile *file,
254 			      GFile *other_file,
255 			      GFileMonitorEvent event_type,
256 			      RhythmDB *db)
257 {
258 	char *canon_uri;
259 	char *other_canon_uri = NULL;
260 	RhythmDBEntry *entry;
261 
262 	canon_uri = g_file_get_uri (file);
263 	if (other_file != NULL) {
264 		other_canon_uri = g_file_get_uri (other_file);
265 	}
266 
267 	rb_debug ("directory event %d for %s", event_type, canon_uri);
268 
269 	switch (event_type) {
270         case G_FILE_MONITOR_EVENT_CREATED:
271 		{
272 			gboolean in_library = FALSE;
273 			int i;
274 
275 			if (!g_settings_get_boolean (db->priv->settings, "monitor-library"))
276 				break;
277 
278 			if (rb_uri_is_hidden (canon_uri))
279 				break;
280 
281 			/* ignore new files outside of the library locations */
282 			for (i = 0; db->priv->library_locations[i] != NULL; i++) {
283 				if (g_str_has_prefix (canon_uri, db->priv->library_locations[i])) {
284 					in_library = TRUE;
285 					break;
286 				}
287 			}
288 
289 			if (!in_library)
290 				break;
291 		}
292 
293 		/* process directories immediately */
294 		if (rb_uri_is_directory (canon_uri)) {
295 			actually_add_monitor (db, file, NULL);
296 			rhythmdb_add_uri (db, canon_uri);
297 		} else {
298 			add_changed_file (db, canon_uri);
299 		}
300 		break;
301 	case G_FILE_MONITOR_EVENT_CHANGED:
302         case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
303 		if (rhythmdb_entry_lookup_by_location (db, canon_uri)) {
304 			add_changed_file (db, canon_uri);
305 		}
306 		break;
307 	case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
308 		/* hmm.. */
309 		break;
310 	case G_FILE_MONITOR_EVENT_DELETED:
311 		entry = rhythmdb_entry_lookup_by_location (db, canon_uri);
312 		if (entry != NULL) {
313 			g_hash_table_remove (db->priv->changed_files, entry->location);
314 			rhythmdb_entry_set_visibility (db, entry, FALSE);
315 			rhythmdb_commit (db);
316 		}
317 		break;
318 	case G_FILE_MONITOR_EVENT_MOVED:
319 		if (other_canon_uri == NULL) {
320 			break;
321 		}
322 
323 		entry = rhythmdb_entry_lookup_by_location (db, other_canon_uri);
324 		if (entry != NULL) {
325 			rb_debug ("file move target %s already exists in database", other_canon_uri);
326 			entry = rhythmdb_entry_lookup_by_location (db, canon_uri);
327 			if (entry != NULL) {
328 				g_hash_table_remove (db->priv->changed_files, entry->location);
329 				rhythmdb_entry_set_visibility (db, entry, FALSE);
330 				rhythmdb_commit (db);
331 			}
332 		} else {
333 			entry = rhythmdb_entry_lookup_by_location (db, canon_uri);
334 			if (entry != NULL) {
335 				GValue v = {0,};
336 				g_value_init (&v, G_TYPE_STRING);
337 				g_value_set_string (&v, other_canon_uri);
338 				rhythmdb_entry_set_internal (db, entry, TRUE, RHYTHMDB_PROP_LOCATION, &v);
339 				g_value_unset (&v);
340 			}
341 		}
342 		break;
343 	case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
344 	case G_FILE_MONITOR_EVENT_UNMOUNTED:
345 	default:
346 		break;
347 	}
348 
349 	g_free (canon_uri);
350 	g_free (other_canon_uri);
351 }
352 
353 void
354 rhythmdb_monitor_uri_path (RhythmDB *db, const char *uri, GError **error)
355 {
356 	GFile *directory;
357 
358 	if (rb_uri_is_directory (uri)) {
359 		char *dir;
360 		if (g_str_has_suffix(uri, G_DIR_SEPARATOR_S)) {
361 			dir = g_strdup (uri);
362 		} else {
363 			dir = g_strconcat (uri, G_DIR_SEPARATOR_S, NULL);
364 		}
365 
366 		directory = g_file_new_for_uri (dir);
367 		g_free (dir);
368 	} else {
369 		GFile *file;
370 
371 		file = g_file_new_for_uri (uri);
372 		directory = g_file_get_parent (file);
373 		g_object_unref (file);
374 	}
375 
376 	actually_add_monitor (db, directory, error);
377 	g_object_unref (directory);
378 }
379 
380 static void
381 rhythmdb_mount_added_cb (GVolumeMonitor *monitor,
382 			 GMount *mount,
383 			 RhythmDB *db)
384 {
385 	GList *l;
386 	RhythmDBQueryResultList *list;
387 	char *mountpoint;
388 	GFile *root;
389 
390 	root = g_mount_get_root (mount);
391 	mountpoint = g_file_get_uri (root);
392 	rb_debug ("volume %s mounted", mountpoint);
393 	g_object_unref (root);
394 
395 	list = rhythmdb_query_result_list_new ();
396 	rhythmdb_do_full_query (db,
397 				RHYTHMDB_QUERY_RESULTS (list),
398 				RHYTHMDB_QUERY_PROP_EQUALS,
399 				  RHYTHMDB_PROP_TYPE,
400 				  RHYTHMDB_ENTRY_TYPE_SONG,
401 				RHYTHMDB_QUERY_PROP_EQUALS,
402 				  RHYTHMDB_PROP_MOUNTPOINT,
403 				  mountpoint,
404 				RHYTHMDB_QUERY_END);
405 	l = rhythmdb_query_result_list_get_results (list);
406 	rb_debug ("%d mounted entries to process", g_list_length (l));
407 	for (; l != NULL; l = l->next) {
408 		RhythmDBEntry *entry = l->data;
409 		const char *location = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
410 
411 		rhythmdb_entry_update_availability (entry, RHYTHMDB_ENTRY_AVAIL_MOUNTED);
412 		if (rb_uri_is_local (location)) {
413 			rhythmdb_add_uri_with_types (db,
414 						     location,
415 						     RHYTHMDB_ENTRY_TYPE_SONG,
416 						     RHYTHMDB_ENTRY_TYPE_IGNORE,
417 						     RHYTHMDB_ENTRY_TYPE_IMPORT_ERROR);
418 		}
419 	}
420 	g_object_unref (list);
421 	g_free (mountpoint);
422 	rhythmdb_commit (db);
423 }
424 
425 static void
426 process_unmounted_entries (RhythmDB *db, RhythmDBEntryType *entry_type, const char *mountpoint)
427 {
428 	RhythmDBQueryResultList *list;
429 	GList *l;
430 
431 	list = rhythmdb_query_result_list_new ();
432 	rhythmdb_do_full_query (db,
433 				RHYTHMDB_QUERY_RESULTS (list),
434 				RHYTHMDB_QUERY_PROP_EQUALS,
435 				  RHYTHMDB_PROP_TYPE,
436 				  entry_type,
437 				RHYTHMDB_QUERY_PROP_EQUALS,
438 				  RHYTHMDB_PROP_MOUNTPOINT,
439 				  mountpoint,
440 				RHYTHMDB_QUERY_END);
441 	l = rhythmdb_query_result_list_get_results (list);
442 	rb_debug ("%d unmounted entries to process", g_list_length (l));
443 	for (; l != NULL; l = l->next) {
444 		RhythmDBEntry *entry = l->data;
445 		rhythmdb_entry_update_availability (entry, RHYTHMDB_ENTRY_AVAIL_UNMOUNTED);
446 	}
447 	g_object_unref (list);
448 	rhythmdb_commit (db);
449 }
450 
451 static void
452 rhythmdb_mount_removed_cb (GVolumeMonitor *monitor,
453 			   GMount *mount,
454 			   RhythmDB *db)
455 {
456 	char *mountpoint;
457 	GFile *root;
458 
459 	root = g_mount_get_root (mount);
460 	mountpoint = g_file_get_uri (root);
461 	rb_debug ("volume %s unmounted", mountpoint);
462 	g_object_unref (root);
463 
464 	process_unmounted_entries (db, RHYTHMDB_ENTRY_TYPE_SONG, mountpoint);
465 	process_unmounted_entries (db, RHYTHMDB_ENTRY_TYPE_IMPORT_ERROR, mountpoint);
466 	g_free (mountpoint);
467 }
468 
469 GList *
470 rhythmdb_get_active_mounts (RhythmDB *db)
471 {
472 	GList *mounts;
473 	GList *mountpoints = NULL;
474 	GList *i;
475 
476 	mounts = g_volume_monitor_get_mounts (db->priv->volume_monitor);
477 	for (i = mounts; i != NULL; i = i->next) {
478 		GFile *root;
479 		char *mountpoint;
480 		GMount *mount = i->data;
481 
482 		root = g_mount_get_root (mount);
483 		mountpoint = g_file_get_uri (root);
484 		mountpoints = g_list_prepend (mountpoints, mountpoint);
485 		g_object_unref (root);
486 	}
487 
488 	rb_list_destroy_free (mounts, (GDestroyNotify) g_object_unref);
489 	return mountpoints;
490 }