No issues found
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 |
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 }