~matttbe/ubuntu/raring/rhythmbox/lp1010619_RB_2.98

« back to all changes in this revision

Viewing changes to .pc/git_crash_during_monitor.patch/rhythmdb/rhythmdb-monitor.c

  • Committer: Package Import Robot
  • Author(s): Michael Terry, Martin Pitt, Jeremy Bicha, Michael Terry
  • Date: 2012-09-19 14:30:03 UTC
  • Revision ID: package-import@ubuntu.com-20120919143003-vt0nuzaodsgxe9k4
Tags: 2.97-1ubuntu5
[ Martin Pitt ]
* debian/source_rhythmbox.py: Fix package name, it's rhythmbox-ubuntuone
  now.

[ Jeremy Bicha ]
* debian/rhythmbox.gsettings-override:
  - Dropped, override moved to ubuntu-settings

[ Michael Terry ]
* debian/patches/git_crash_during_monitor.patch:
  - Backport git patches to guard against crashing due to monitoring
    files outside library (by moving a g_strfreev and also just stopping
    monitoring such files at all).  LP: #1029333

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
#if !GLIB_CHECK_VERSION(2,24,0)
 
46
#define G_FILE_MONITOR_SEND_MOVED       0
 
47
#endif
 
48
 
 
49
#define RHYTHMDB_FILE_MODIFY_PROCESS_TIME 2
 
50
 
 
51
static void rhythmdb_directory_change_cb (GFileMonitor *monitor,
 
52
                                          GFile *file,
 
53
                                          GFile *other_file,
 
54
                                          GFileMonitorEvent event_type,
 
55
                                          RhythmDB *db);
 
56
static void rhythmdb_mount_added_cb (GVolumeMonitor *monitor,
 
57
                                     GMount *mount,
 
58
                                     RhythmDB *db);
 
59
static void rhythmdb_mount_removed_cb (GVolumeMonitor *monitor,
 
60
                                       GMount *mount,
 
61
                                       RhythmDB *db);
 
62
 
 
63
void
 
64
rhythmdb_init_monitoring (RhythmDB *db)
 
65
{
 
66
        db->priv->monitor_mutex = g_mutex_new ();
 
67
 
 
68
        db->priv->monitored_directories = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal,
 
69
                                                                 (GDestroyNotify) g_object_unref,
 
70
                                                                 (GDestroyNotify)g_file_monitor_cancel);
 
71
 
 
72
        db->priv->changed_files = g_hash_table_new_full (rb_refstring_hash, rb_refstring_equal,
 
73
                                                         (GDestroyNotify) rb_refstring_unref,
 
74
                                                         NULL);
 
75
 
 
76
        db->priv->volume_monitor = g_volume_monitor_get ();
 
77
        g_signal_connect (G_OBJECT (db->priv->volume_monitor),
 
78
                          "mount-added",
 
79
                          G_CALLBACK (rhythmdb_mount_added_cb),
 
80
                          db);
 
81
 
 
82
        g_signal_connect (G_OBJECT (db->priv->volume_monitor),
 
83
                          "mount-removed",
 
84
                          G_CALLBACK (rhythmdb_mount_removed_cb),
 
85
                          db);
 
86
        g_signal_connect (G_OBJECT (db->priv->volume_monitor),
 
87
                          "mount-pre-unmount",
 
88
                          G_CALLBACK (rhythmdb_mount_removed_cb),
 
89
                          db);
 
90
}
 
91
 
 
92
void
 
93
rhythmdb_dispose_monitoring (RhythmDB *db)
 
94
{
 
95
        if (db->priv->changed_files_id != 0) {
 
96
                g_source_remove (db->priv->changed_files_id);
 
97
                db->priv->changed_files_id = 0;
 
98
        }
 
99
 
 
100
        if (db->priv->volume_monitor != NULL) {
 
101
                g_object_unref (db->priv->volume_monitor);
 
102
                db->priv->volume_monitor = NULL;
 
103
        }
 
104
}
 
105
 
 
106
void
 
107
rhythmdb_finalize_monitoring (RhythmDB *db)
 
108
{
 
109
        rhythmdb_stop_monitoring (db);
 
110
 
 
111
        g_hash_table_destroy (db->priv->monitored_directories);
 
112
        g_hash_table_destroy (db->priv->changed_files);
 
113
 
 
114
        g_mutex_free (db->priv->monitor_mutex);
 
115
}
 
116
 
 
117
void
 
118
rhythmdb_stop_monitoring (RhythmDB *db)
 
119
{
 
120
        g_hash_table_foreach_remove (db->priv->monitored_directories,
 
121
                                     (GHRFunc) rb_true_function,
 
122
                                     db);
 
123
}
 
124
 
 
125
static void
 
126
actually_add_monitor (RhythmDB *db, GFile *directory, GError **error)
 
127
{
 
128
        GFileMonitor *monitor;
 
129
 
 
130
        if (directory == NULL) {
 
131
                return;
 
132
        }
 
133
 
 
134
        g_mutex_lock (db->priv->monitor_mutex);
 
135
 
 
136
        if (g_hash_table_lookup (db->priv->monitored_directories, directory)) {
 
137
                g_mutex_unlock (db->priv->monitor_mutex);
 
138
                return;
 
139
        }
 
140
 
 
141
        monitor = g_file_monitor_directory (directory, G_FILE_MONITOR_SEND_MOVED, db->priv->exiting, error);
 
142
        if (monitor != NULL) {
 
143
                g_signal_connect_object (G_OBJECT (monitor),
 
144
                                         "changed",
 
145
                                         G_CALLBACK (rhythmdb_directory_change_cb),
 
146
                                         db, 0);
 
147
                g_hash_table_insert (db->priv->monitored_directories,
 
148
                                     g_object_ref (directory),
 
149
                                     monitor);
 
150
        }
 
151
 
 
152
        g_mutex_unlock (db->priv->monitor_mutex);
 
153
}
 
154
 
 
155
static void
 
156
monitor_entry_file (RhythmDBEntry *entry, RhythmDB *db)
 
157
{
 
158
        if (entry->type == RHYTHMDB_ENTRY_TYPE_SONG) {
 
159
                const char *loc;
 
160
                int i;
 
161
 
 
162
                loc = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
 
163
 
 
164
                /* don't add add monitor if it's in the library path */
 
165
                for (i = 0; db->priv->library_locations[i] != NULL; i++) {
 
166
                        if (g_str_has_prefix (loc, db->priv->library_locations[i]))
 
167
                                return;
 
168
                }
 
169
                rhythmdb_monitor_uri_path (db, loc, NULL);
 
170
        }
 
171
}
 
172
 
 
173
static gboolean
 
174
monitor_subdirectory (GFile *file, gboolean dir, RhythmDB *db)
 
175
{
 
176
        char *uri;
 
177
 
 
178
        uri = g_file_get_uri (file);
 
179
        if (dir) {
 
180
                actually_add_monitor (db, file, NULL);
 
181
        } else {
 
182
                /* add the file to the database if it's not already there */
 
183
                RhythmDBEntry *entry;
 
184
 
 
185
                entry = rhythmdb_entry_lookup_by_location (db, uri);
 
186
                if (entry == NULL) {
 
187
                        rhythmdb_add_uri (db, uri);
 
188
                }
 
189
        }
 
190
        g_free (uri);
 
191
        return TRUE;    
 
192
}
 
193
 
 
194
static void
 
195
monitor_library_directory (const char *uri, RhythmDB *db)
 
196
{
 
197
        if ((strcmp (uri, "file:///") == 0) ||
 
198
            (strcmp (uri, "file://") == 0)) {
 
199
                /* display an error to the user? */
 
200
                return;
 
201
        }
 
202
 
 
203
        rb_debug ("beginning monitor of the library directory %s", uri);
 
204
        rhythmdb_monitor_uri_path (db, uri, NULL);
 
205
        rb_uri_handle_recursively_async (uri,
 
206
                                         NULL,
 
207
                                         (RBUriRecurseFunc) monitor_subdirectory,
 
208
                                         g_object_ref (db),
 
209
                                         (GDestroyNotify)g_object_unref);
 
210
}
 
211
 
 
212
static gboolean
 
213
rhythmdb_check_changed_file (RBRefString *uri, gpointer data, RhythmDB *db)
 
214
{
 
215
        GTimeVal time;
 
216
        glong time_sec = GPOINTER_TO_INT (data);
 
217
 
 
218
        g_get_current_time (&time);
 
219
        if (time.tv_sec >= time_sec + RHYTHMDB_FILE_MODIFY_PROCESS_TIME) {
 
220
                rb_debug ("adding newly located file %s", rb_refstring_get (uri));
 
221
                rhythmdb_add_uri (db, rb_refstring_get (uri));
 
222
                return TRUE;
 
223
        }
 
224
 
 
225
        rb_debug ("waiting to add newly located file %s", rb_refstring_get (uri));
 
226
 
 
227
        return FALSE;
 
228
}
 
229
 
 
230
static gboolean
 
231
rhythmdb_process_changed_files (RhythmDB *db)
 
232
{
 
233
        /*
 
234
         * no need for a mutex around the changed files map as it's only accessed
 
235
         * from the main thread.  GFileMonitor's 'changed' signal is emitted from an
 
236
         * idle handler, and we only process the map in a timeout callback.
 
237
         */
 
238
        if (g_hash_table_size (db->priv->changed_files) == 0) {
 
239
                db->priv->changed_files_id = 0;
 
240
                return FALSE;
 
241
        }
 
242
 
 
243
        g_hash_table_foreach_remove (db->priv->changed_files,
 
244
                                     (GHRFunc)rhythmdb_check_changed_file, db);
 
245
        return TRUE;
 
246
}
 
247
 
 
248
static gpointer
 
249
_monitor_entry_thread (RhythmDB *db)
 
250
{
 
251
        rhythmdb_entry_foreach (db, (GFunc) monitor_entry_file, db);
 
252
        g_object_unref (G_OBJECT (db));
 
253
        return NULL;
 
254
}
 
255
 
 
256
void
 
257
rhythmdb_start_monitoring (RhythmDB *db)
 
258
{
 
259
        g_thread_create ((GThreadFunc)_monitor_entry_thread, g_object_ref (db), FALSE, NULL);
 
260
 
 
261
        /* monitor all library locations */
 
262
        if (db->priv->library_locations) {
 
263
                int i;
 
264
                for (i = 0; db->priv->library_locations[i] != NULL; i++) {
 
265
                        monitor_library_directory (db->priv->library_locations[i], db);
 
266
                }
 
267
        }
 
268
}
 
269
 
 
270
static void
 
271
add_changed_file (RhythmDB *db, const char *uri)
 
272
{
 
273
        GTimeVal time;
 
274
 
 
275
        g_get_current_time (&time);
 
276
        g_hash_table_replace (db->priv->changed_files,
 
277
                              rb_refstring_new (uri),
 
278
                              GINT_TO_POINTER (time.tv_sec));
 
279
        if (db->priv->changed_files_id == 0) {
 
280
                db->priv->changed_files_id =
 
281
                        g_timeout_add_seconds (RHYTHMDB_FILE_MODIFY_PROCESS_TIME,
 
282
                                               (GSourceFunc) rhythmdb_process_changed_files,
 
283
                                               db);
 
284
        }
 
285
}
 
286
 
 
287
static void
 
288
rhythmdb_directory_change_cb (GFileMonitor *monitor,
 
289
                              GFile *file,
 
290
                              GFile *other_file,
 
291
                              GFileMonitorEvent event_type,
 
292
                              RhythmDB *db)
 
293
{
 
294
        char *canon_uri;
 
295
        char *other_canon_uri = NULL;
 
296
        RhythmDBEntry *entry;
 
297
 
 
298
        canon_uri = g_file_get_uri (file);
 
299
        if (other_file != NULL) {
 
300
                other_canon_uri = g_file_get_uri (other_file);
 
301
        }
 
302
 
 
303
        rb_debug ("directory event %d for %s", event_type, canon_uri);
 
304
 
 
305
        switch (event_type) {
 
306
        case G_FILE_MONITOR_EVENT_CREATED:
 
307
                {
 
308
                        gboolean in_library = FALSE;
 
309
                        int i;
 
310
 
 
311
                        if (!g_settings_get_boolean (db->priv->settings, "monitor-library"))
 
312
                                break;
 
313
 
 
314
                        if (rb_uri_is_hidden (canon_uri))
 
315
                                break;
 
316
 
 
317
                        /* ignore new files outside of the library locations */
 
318
                        for (i = 0; db->priv->library_locations[i] != NULL; i++) {
 
319
                                if (g_str_has_prefix (canon_uri, db->priv->library_locations[i])) {
 
320
                                        in_library = TRUE;
 
321
                                        break;
 
322
                                }
 
323
                        }
 
324
 
 
325
                        if (!in_library)
 
326
                                break;
 
327
                }
 
328
 
 
329
                /* process directories immediately */
 
330
                if (rb_uri_is_directory (canon_uri)) {
 
331
                        actually_add_monitor (db, file, NULL);
 
332
                        rhythmdb_add_uri (db, canon_uri);
 
333
                } else {
 
334
                        add_changed_file (db, canon_uri);
 
335
                }
 
336
                break;
 
337
        case G_FILE_MONITOR_EVENT_CHANGED:
 
338
        case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
 
339
                if (rhythmdb_entry_lookup_by_location (db, canon_uri)) {
 
340
                        add_changed_file (db, canon_uri);
 
341
                }
 
342
                break;
 
343
        case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
 
344
                /* hmm.. */
 
345
                break;
 
346
        case G_FILE_MONITOR_EVENT_DELETED:
 
347
                entry = rhythmdb_entry_lookup_by_location (db, canon_uri);
 
348
                if (entry != NULL) {
 
349
                        g_hash_table_remove (db->priv->changed_files, entry->location);
 
350
                        rhythmdb_entry_set_visibility (db, entry, FALSE);
 
351
                        rhythmdb_commit (db);
 
352
                }
 
353
                break;
 
354
#if GLIB_CHECK_VERSION(2,24,0)
 
355
        case G_FILE_MONITOR_EVENT_MOVED:
 
356
                if (other_canon_uri == NULL) {
 
357
                        break;
 
358
                }
 
359
 
 
360
                entry = rhythmdb_entry_lookup_by_location (db, other_canon_uri);
 
361
                if (entry != NULL) {
 
362
                        rb_debug ("file move target %s already exists in database", other_canon_uri);
 
363
                        entry = rhythmdb_entry_lookup_by_location (db, canon_uri);
 
364
                        if (entry != NULL) {
 
365
                                g_hash_table_remove (db->priv->changed_files, entry->location);
 
366
                                rhythmdb_entry_set_visibility (db, entry, FALSE);
 
367
                                rhythmdb_commit (db);
 
368
                        }
 
369
                } else {
 
370
                        entry = rhythmdb_entry_lookup_by_location (db, canon_uri);
 
371
                        if (entry != NULL) {
 
372
                                GValue v = {0,};
 
373
                                g_value_init (&v, G_TYPE_STRING);
 
374
                                g_value_set_string (&v, other_canon_uri);
 
375
                                rhythmdb_entry_set_internal (db, entry, TRUE, RHYTHMDB_PROP_LOCATION, &v);
 
376
                                g_value_unset (&v);
 
377
                        }
 
378
                }
 
379
                break;
 
380
#endif
 
381
        case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
 
382
        case G_FILE_MONITOR_EVENT_UNMOUNTED:
 
383
        default:
 
384
                break;
 
385
        }
 
386
 
 
387
        g_free (canon_uri);
 
388
        g_free (other_canon_uri);
 
389
}
 
390
 
 
391
void
 
392
rhythmdb_monitor_uri_path (RhythmDB *db, const char *uri, GError **error)
 
393
{
 
394
        GFile *directory;
 
395
 
 
396
        if (rb_uri_is_directory (uri)) {
 
397
                char *dir;
 
398
                if (g_str_has_suffix(uri, G_DIR_SEPARATOR_S)) {
 
399
                        dir = g_strdup (uri);
 
400
                } else {
 
401
                        dir = g_strconcat (uri, G_DIR_SEPARATOR_S, NULL);
 
402
                }
 
403
 
 
404
                directory = g_file_new_for_uri (dir);
 
405
                g_free (dir);
 
406
        } else {
 
407
                GFile *file;
 
408
 
 
409
                file = g_file_new_for_uri (uri);
 
410
                directory = g_file_get_parent (file);
 
411
                g_object_unref (file);
 
412
        }
 
413
 
 
414
        actually_add_monitor (db, directory, error);
 
415
        g_object_unref (directory);
 
416
}
 
417
 
 
418
static void
 
419
rhythmdb_mount_added_cb (GVolumeMonitor *monitor,
 
420
                         GMount *mount,
 
421
                         RhythmDB *db)
 
422
{
 
423
        GList *l;
 
424
        RhythmDBQueryResultList *list;
 
425
        char *mountpoint;
 
426
        GFile *root;
 
427
 
 
428
        root = g_mount_get_root (mount);
 
429
        mountpoint = g_file_get_uri (root);
 
430
        rb_debug ("volume %s mounted", mountpoint);
 
431
        g_object_unref (root);
 
432
 
 
433
        list = rhythmdb_query_result_list_new ();
 
434
        rhythmdb_do_full_query (db,
 
435
                                RHYTHMDB_QUERY_RESULTS (list),
 
436
                                RHYTHMDB_QUERY_PROP_EQUALS,
 
437
                                  RHYTHMDB_PROP_TYPE,
 
438
                                  RHYTHMDB_ENTRY_TYPE_SONG,
 
439
                                RHYTHMDB_QUERY_PROP_EQUALS,
 
440
                                  RHYTHMDB_PROP_MOUNTPOINT,
 
441
                                  mountpoint,
 
442
                                RHYTHMDB_QUERY_END);
 
443
        l = rhythmdb_query_result_list_get_results (list);
 
444
        rb_debug ("%d mounted entries to process", g_list_length (l));
 
445
        for (; l != NULL; l = l->next) {
 
446
                RhythmDBEntry *entry = l->data;
 
447
                const char *location = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
 
448
 
 
449
                rhythmdb_entry_update_availability (entry, RHYTHMDB_ENTRY_AVAIL_MOUNTED);
 
450
                if (rb_uri_is_local (location)) {
 
451
                        rhythmdb_add_uri_with_types (db,
 
452
                                                     location,
 
453
                                                     RHYTHMDB_ENTRY_TYPE_SONG,
 
454
                                                     RHYTHMDB_ENTRY_TYPE_IGNORE,
 
455
                                                     RHYTHMDB_ENTRY_TYPE_IMPORT_ERROR);
 
456
                }
 
457
        }
 
458
        g_object_unref (list);
 
459
        g_free (mountpoint);
 
460
        rhythmdb_commit (db);
 
461
}
 
462
 
 
463
static void
 
464
process_unmounted_entries (RhythmDB *db, RhythmDBEntryType *entry_type, const char *mountpoint)
 
465
{
 
466
        RhythmDBQueryResultList *list;
 
467
        GList *l;
 
468
 
 
469
        list = rhythmdb_query_result_list_new ();
 
470
        rhythmdb_do_full_query (db,
 
471
                                RHYTHMDB_QUERY_RESULTS (list),
 
472
                                RHYTHMDB_QUERY_PROP_EQUALS,
 
473
                                  RHYTHMDB_PROP_TYPE,
 
474
                                  entry_type,
 
475
                                RHYTHMDB_QUERY_PROP_EQUALS,
 
476
                                  RHYTHMDB_PROP_MOUNTPOINT,
 
477
                                  mountpoint,
 
478
                                RHYTHMDB_QUERY_END);
 
479
        l = rhythmdb_query_result_list_get_results (list);
 
480
        rb_debug ("%d unmounted entries to process", g_list_length (l));
 
481
        for (; l != NULL; l = l->next) {
 
482
                RhythmDBEntry *entry = l->data;
 
483
                rhythmdb_entry_update_availability (entry, RHYTHMDB_ENTRY_AVAIL_UNMOUNTED);
 
484
        }
 
485
        g_object_unref (list);
 
486
        rhythmdb_commit (db);
 
487
}
 
488
 
 
489
static void
 
490
rhythmdb_mount_removed_cb (GVolumeMonitor *monitor,
 
491
                           GMount *mount,
 
492
                           RhythmDB *db)
 
493
{
 
494
        char *mountpoint;
 
495
        GFile *root;
 
496
 
 
497
        root = g_mount_get_root (mount);
 
498
        mountpoint = g_file_get_uri (root);
 
499
        rb_debug ("volume %s unmounted", mountpoint);
 
500
        g_object_unref (root);
 
501
 
 
502
        process_unmounted_entries (db, RHYTHMDB_ENTRY_TYPE_SONG, mountpoint);
 
503
        process_unmounted_entries (db, RHYTHMDB_ENTRY_TYPE_IMPORT_ERROR, mountpoint);
 
504
        g_free (mountpoint);
 
505
}
 
506
 
 
507
GList *
 
508
rhythmdb_get_active_mounts (RhythmDB *db)
 
509
{
 
510
        GList *mounts;
 
511
        GList *mountpoints = NULL;
 
512
        GList *i;
 
513
 
 
514
        mounts = g_volume_monitor_get_mounts (db->priv->volume_monitor);
 
515
        for (i = mounts; i != NULL; i = i->next) {
 
516
                GFile *root;
 
517
                char *mountpoint;
 
518
                GMount *mount = i->data;
 
519
 
 
520
                root = g_mount_get_root (mount);
 
521
                mountpoint = g_file_get_uri (root);
 
522
                mountpoints = g_list_prepend (mountpoints, mountpoint);
 
523
                g_object_unref (root);
 
524
        }
 
525
 
 
526
        rb_list_destroy_free (mounts, (GDestroyNotify) g_object_unref);
 
527
        return mountpoints;
 
528
}