~ubuntu-branches/ubuntu/saucy/evolution-data-server/saucy

« back to all changes in this revision

Viewing changes to modules/cache-reaper/module-cache-reaper.c

  • Committer: Package Import Robot
  • Author(s): Chris Coulson
  • Date: 2012-10-08 12:58:16 UTC
  • mfrom: (181.1.7 quantal)
  • Revision ID: package-import@ubuntu.com-20121008125816-i3n76e8c0m64e7xp
Tags: 3.6.0-0ubuntu2
* Fix LP: #1038047 part 1 - Don't abort in e_source_registry_new* when a
  problem occurs connecting to the Dbus service
  - add debian/patches/dont-abort-in-e_source_registry_new.patch
  - update debian/patches/series
* Fix LP: #1038047 part 2 - libedataserver depends on
  evolution-data-server-common to ensure that the GSettings schemas are
  present
  - update debian/control

Show diffs side-by-side

added added

removed removed

Lines of Context:
31
31
        (G_TYPE_CHECK_INSTANCE_CAST \
32
32
        ((obj), E_TYPE_CACHE_REAPER, ECacheReaper))
33
33
 
34
 
/* Where abandoned cache directories go to die. */
 
34
/* Where abandoned directories go to die. */
35
35
#define TRASH_DIRECTORY_NAME "trash"
36
36
 
37
37
/* XXX These intervals are rather arbitrary and prone to bikeshedding.
38
38
 *     It's just what I decided on.  On startup we wait an hour to reap
39
 
 *     abandoned cache directories, and thereafter repeat every 24 hours. */
 
39
 *     abandoned directories, and thereafter repeat every 24 hours. */
40
40
#define INITIAL_INTERVAL_SECONDS  ( 1 * (60 * 60))
41
41
#define REGULAR_INTERVAL_SECONDS  (24 * (60 * 60))
42
42
 
 
43
/* XXX Similarly, these expiry times are rather arbitrary and prone to
 
44
 *     bikeshedding.  Most importantly, the expiry for data directories
 
45
 *     should be far more conservative (longer) than cache directories.
 
46
 *     Cache directories are disposable, data directories are not, so
 
47
 *     we want to let abandoned data directories linger longer. */
 
48
 
 
49
/* Minimum days for a data directory
 
50
 * to live in trash before reaping it. */
 
51
#define DATA_EXPIRY_IN_DAYS 28
 
52
 
 
53
/* Minimum days for a cache directory
 
54
 * to live in trash before reaping it. */
 
55
#define CACHE_EXPIRY_IN_DAYS 7
 
56
 
43
57
typedef struct _ECacheReaper ECacheReaper;
44
58
typedef struct _ECacheReaperClass ECacheReaperClass;
45
59
 
46
60
struct _ECacheReaper {
47
61
        EExtension parent;
48
62
 
49
 
        guint n_directories;
 
63
        guint n_data_directories;
 
64
        GFile **data_directories;
 
65
        GFile **data_trash_directories;
 
66
 
 
67
        guint n_cache_directories;
50
68
        GFile **cache_directories;
51
 
        GFile **trash_directories;
 
69
        GFile **cache_trash_directories;
52
70
 
53
71
        guint reaping_timeout_id;
54
72
};
79
97
        return E_SOURCE_REGISTRY_SERVER (extensible);
80
98
}
81
99
 
 
100
static gboolean
 
101
cache_reaper_make_directory_and_parents (GFile *directory,
 
102
                                         GCancellable *cancellable,
 
103
                                         GError **error)
 
104
{
 
105
        gboolean success;
 
106
        GError *local_error = NULL;
 
107
 
 
108
        /* XXX Maybe add some function like this to libedataserver.
 
109
         *     It's annoying to always have to check for and clear
 
110
         *     G_IO_ERROR_EXISTS when ensuring a directory exists. */
 
111
 
 
112
        success = g_file_make_directory_with_parents (
 
113
                directory, cancellable, &local_error);
 
114
 
 
115
        if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
 
116
                g_clear_error (&local_error);
 
117
 
 
118
        if (local_error != NULL) {
 
119
                gchar *path;
 
120
 
 
121
                g_propagate_error (error, local_error);
 
122
 
 
123
                path = g_file_get_path (directory);
 
124
                g_prefix_error (
 
125
                        error, "Failed to make directory '%s': ", path);
 
126
                g_free (path);
 
127
        }
 
128
 
 
129
        return success;
 
130
}
 
131
 
82
132
static void
83
133
cache_reaper_trash_directory_reaped (GObject *source_object,
84
134
                                     GAsyncResult *result,
112
162
        ECacheReaper *extension = E_CACHE_REAPER (user_data);
113
163
        guint ii;
114
164
 
 
165
        g_message ("Reaping abandoned data directories");
 
166
 
 
167
        for (ii = 0; ii < extension->n_data_directories; ii++)
 
168
                e_reap_trash_directory (
 
169
                        extension->data_trash_directories[ii],
 
170
                        DATA_EXPIRY_IN_DAYS,
 
171
                        G_PRIORITY_LOW, NULL,
 
172
                        cache_reaper_trash_directory_reaped,
 
173
                        NULL);
 
174
 
115
175
        g_message ("Reaping abandoned cache directories");
116
176
 
117
 
        for (ii = 0; ii < extension->n_directories; ii++)
 
177
        for (ii = 0; ii < extension->n_cache_directories; ii++)
118
178
                e_reap_trash_directory (
119
 
                        extension->trash_directories[ii],
 
179
                        extension->cache_trash_directories[ii],
 
180
                        CACHE_EXPIRY_IN_DAYS,
120
181
                        G_PRIORITY_LOW, NULL,
121
182
                        cache_reaper_trash_directory_reaped,
122
183
                        NULL);
178
239
}
179
240
 
180
241
static void
181
 
cache_reaper_scan_cache_directory (ECacheReaper *extension,
182
 
                                   GFile *cache_directory,
183
 
                                   GFile *trash_directory)
 
242
cache_reaper_scan_directory (ECacheReaper *extension,
 
243
                             GFile *base_directory,
 
244
                             GFile *trash_directory)
184
245
{
185
246
        GFileEnumerator *file_enumerator;
186
247
        ESourceRegistryServer *server;
190
251
        server = cache_reaper_get_server (extension);
191
252
 
192
253
        file_enumerator = g_file_enumerate_children (
193
 
                cache_directory,
 
254
                base_directory,
194
255
                G_FILE_ATTRIBUTE_STANDARD_NAME,
195
256
                G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
196
257
                NULL, &error);
215
276
                if (g_strcmp0 (name, TRASH_DIRECTORY_NAME) == 0)
216
277
                        goto next;
217
278
 
 
279
                /* Also skip directories named "system".  For backward
 
280
                 * compatibility, data directories for built-in sources
 
281
                 * are named "system" instead of "system-address-book"
 
282
                 * or "system-calendar" or what have you. */
 
283
                if (g_strcmp0 (name, "system") == 0)
 
284
                        goto next;
 
285
 
218
286
                source = e_source_registry_server_ref_source (server, name);
219
287
 
220
288
                if (source == NULL) {
222
290
                        GFile *target_directory;
223
291
 
224
292
                        source_directory = g_file_get_child (
225
 
                                cache_directory, name);
 
293
                                base_directory, name);
226
294
                        target_directory = g_file_get_child (
227
295
                                trash_directory, name);
228
296
 
248
316
        if (error != NULL) {
249
317
                gchar *path;
250
318
 
251
 
                path = g_file_get_path (cache_directory);
 
319
                path = g_file_get_path (base_directory);
252
320
                g_warning ("Failed to scan '%s': %s", path, error->message);
253
321
                g_free (path);
254
322
 
257
325
}
258
326
 
259
327
static void
 
328
cache_reaper_scan_data_directories (ECacheReaper *extension)
 
329
{
 
330
        guint ii;
 
331
 
 
332
        /* Scan the base data directories for unrecognized subdirectories.
 
333
         * The subdirectories are named after data source UIDs, so compare
 
334
         * their names to registered data sources and move any unrecognized
 
335
         * subdirectories to the "trash" subdirectory to be reaped later. */
 
336
 
 
337
        g_message ("Scanning data directories");
 
338
 
 
339
        for (ii = 0; ii < extension->n_data_directories; ii++)
 
340
                cache_reaper_scan_directory (
 
341
                        extension,
 
342
                        extension->data_directories[ii],
 
343
                        extension->data_trash_directories[ii]);
 
344
}
 
345
 
 
346
static void
260
347
cache_reaper_scan_cache_directories (ECacheReaper *extension)
261
348
{
262
349
        guint ii;
263
350
 
264
 
        /* Scan the base cache directories for unregnized subdirectories.
 
351
        /* Scan the base cache directories for unrecognized subdirectories.
265
352
         * The subdirectories are named after data source UIDs, so compare
266
353
         * their names to registered data sources and move any unrecognized
267
354
         * subdirectories to the "trash" subdirectory to be reaped later. */
268
355
 
269
356
        g_message ("Scanning cache directories");
270
357
 
271
 
        for (ii = 0; ii < extension->n_directories; ii++)
272
 
                cache_reaper_scan_cache_directory (
 
358
        for (ii = 0; ii < extension->n_cache_directories; ii++)
 
359
                cache_reaper_scan_directory (
273
360
                        extension,
274
361
                        extension->cache_directories[ii],
275
 
                        extension->trash_directories[ii]);
 
362
                        extension->cache_trash_directories[ii]);
276
363
}
277
364
 
278
365
static void
279
 
cache_reaper_move_cache_to_trash (ECacheReaper *extension,
280
 
                                  ESource *source,
281
 
                                  GFile *cache_directory,
282
 
                                  GFile *trash_directory)
 
366
cache_reaper_move_to_trash (ECacheReaper *extension,
 
367
                            ESource *source,
 
368
                            GFile *base_directory,
 
369
                            GFile *trash_directory)
283
370
{
284
371
        GFile *source_directory;
285
372
        GFile *target_directory;
287
374
 
288
375
        uid = e_source_get_uid (source);
289
376
 
290
 
        source_directory = g_file_get_child (cache_directory, uid);
 
377
        source_directory = g_file_get_child (base_directory, uid);
291
378
        target_directory = g_file_get_child (trash_directory, uid);
292
379
 
293
380
        /* This is a no-op if the source directory does not exist. */
298
385
}
299
386
 
300
387
static void
301
 
cache_reaper_recover_cache_from_trash (ECacheReaper *extension,
302
 
                                       ESource *source,
303
 
                                       GFile *cache_directory,
304
 
                                       GFile *trash_directory)
 
388
cache_reaper_recover_from_trash (ECacheReaper *extension,
 
389
                                 ESource *source,
 
390
                                 GFile *base_directory,
 
391
                                 GFile *trash_directory)
305
392
{
306
393
        GFile *source_directory;
307
394
        GFile *target_directory;
310
397
        uid = e_source_get_uid (source);
311
398
 
312
399
        source_directory = g_file_get_child (trash_directory, uid);
313
 
        target_directory = g_file_get_child (cache_directory, uid);
 
400
        target_directory = g_file_get_child (base_directory, uid);
314
401
 
315
402
        /* This is a no-op if the source directory does not exist. */
316
403
        cache_reaper_move_directory (source_directory, target_directory);
323
410
cache_reaper_files_loaded_cb (ESourceRegistryServer *server,
324
411
                              ECacheReaper *extension)
325
412
{
 
413
        cache_reaper_scan_data_directories (extension);
326
414
        cache_reaper_scan_cache_directories (extension);
327
415
 
328
416
        /* Schedule the initial reaping. */
343
431
 
344
432
        /* The Cache Reaper is not too proud to dig through the
345
433
         * trash on the off chance the newly-added source has a
346
 
         * recoverable cache directory. */
347
 
        for (ii = 0; ii < extension->n_directories; ii++)
348
 
                cache_reaper_recover_cache_from_trash (
 
434
         * recoverable data or cache directory. */
 
435
 
 
436
        for (ii = 0; ii < extension->n_data_directories; ii++)
 
437
                cache_reaper_recover_from_trash (
 
438
                        extension, source,
 
439
                        extension->data_directories[ii],
 
440
                        extension->data_trash_directories[ii]);
 
441
 
 
442
        for (ii = 0; ii < extension->n_cache_directories; ii++)
 
443
                cache_reaper_recover_from_trash (
349
444
                        extension, source,
350
445
                        extension->cache_directories[ii],
351
 
                        extension->trash_directories[ii]);
 
446
                        extension->cache_trash_directories[ii]);
352
447
}
353
448
 
354
449
static void
358
453
{
359
454
        guint ii;
360
455
 
361
 
        /* Stage the removed source's cache directory for
362
 
         * reaping by moving it to the "trash" directory. */
363
 
        for (ii = 0; ii < extension->n_directories; ii++)
364
 
                cache_reaper_move_cache_to_trash (
 
456
        /* Stage the removed source's cache directory for reaping
 
457
         * by moving it to the "trash" directory.
 
458
         *
 
459
         * Do NOT do this for data directories.  Cache directories
 
460
         * are disposable and can be regenerated from the canonical
 
461
         * data source, but data directories ARE the canonical data
 
462
         * source so we want to be more conservative with them.  If
 
463
         * the removed source has a data directory, we will move it
 
464
         * to the "trash" directory on next registry startup, which
 
465
         * may correspond with the next desktop session startup. */
 
466
 
 
467
        for (ii = 0; ii < extension->n_cache_directories; ii++)
 
468
                cache_reaper_move_to_trash (
365
469
                        extension, source,
366
470
                        extension->cache_directories[ii],
367
 
                        extension->trash_directories[ii]);
 
471
                        extension->cache_trash_directories[ii]);
368
472
}
369
473
 
370
474
static void
375
479
 
376
480
        extension = E_CACHE_REAPER (object);
377
481
 
378
 
        for (ii = 0; ii < extension->n_directories; ii++) {
 
482
        for (ii = 0; ii < extension->n_data_directories; ii++) {
 
483
                g_object_unref (extension->data_directories[ii]);
 
484
                g_object_unref (extension->data_trash_directories[ii]);
 
485
        }
 
486
 
 
487
        g_free (extension->data_directories);
 
488
        g_free (extension->data_trash_directories);
 
489
 
 
490
        for (ii = 0; ii < extension->n_cache_directories; ii++) {
379
491
                g_object_unref (extension->cache_directories[ii]);
380
 
                g_object_unref (extension->trash_directories[ii]);
 
492
                g_object_unref (extension->cache_trash_directories[ii]);
381
493
        }
382
494
 
383
495
        g_free (extension->cache_directories);
384
 
        g_free (extension->trash_directories);
 
496
        g_free (extension->cache_trash_directories);
385
497
 
386
498
        if (extension->reaping_timeout_id > 0)
387
499
                g_source_remove (extension->reaping_timeout_id);
438
550
e_cache_reaper_init (ECacheReaper *extension)
439
551
{
440
552
        GFile *base_directory;
 
553
        const gchar *user_data_dir;
441
554
        const gchar *user_cache_dir;
442
555
        guint n_directories, ii;
443
556
 
444
557
        /* These are component names from which
 
558
         * the data directory arrays are built. */
 
559
        const gchar *data_component_names[] = {
 
560
                "addressbook",
 
561
                "calendar",
 
562
                "mail",
 
563
                "memos",
 
564
                "tasks"
 
565
        };
 
566
 
 
567
        /* These are component names from which
445
568
         * the cache directory arrays are built. */
446
 
        const gchar *component_names[] = {
 
569
        const gchar *cache_component_names[] = {
447
570
                "addressbook",
448
571
                "calendar",
449
572
                "mail",
452
575
                "tasks"
453
576
        };
454
577
 
455
 
        n_directories = G_N_ELEMENTS (component_names);
456
 
 
457
 
        extension->n_directories = n_directories;
 
578
        /* Setup base directories for data. */
 
579
 
 
580
        n_directories = G_N_ELEMENTS (data_component_names);
 
581
 
 
582
        extension->n_data_directories = n_directories;
 
583
        extension->data_directories = g_new0 (GFile *, n_directories);
 
584
        extension->data_trash_directories = g_new0 (GFile *, n_directories);
 
585
 
 
586
        user_data_dir = e_get_user_data_dir ();
 
587
        base_directory = g_file_new_for_path (user_data_dir);
 
588
 
 
589
        for (ii = 0; ii < n_directories; ii++) {
 
590
                GFile *data_directory;
 
591
                GFile *trash_directory;
 
592
                GError *error = NULL;
 
593
 
 
594
                data_directory = g_file_get_child (
 
595
                        base_directory, data_component_names[ii]);
 
596
                trash_directory = g_file_get_child (
 
597
                        data_directory, TRASH_DIRECTORY_NAME);
 
598
 
 
599
                /* Data directory is a parent of the trash
 
600
                 * directory so this is sufficient for both. */
 
601
                cache_reaper_make_directory_and_parents (
 
602
                        trash_directory, NULL, &error);
 
603
 
 
604
                if (error != NULL) {
 
605
                        g_warning ("%s: %s", G_STRFUNC, error->message);
 
606
                        g_error_free (error);
 
607
                }
 
608
 
 
609
                extension->data_directories[ii] = data_directory;
 
610
                extension->data_trash_directories[ii] = trash_directory;
 
611
        }
 
612
 
 
613
        g_object_unref (base_directory);
 
614
 
 
615
        /* Setup base directories for cache. */
 
616
 
 
617
        n_directories = G_N_ELEMENTS (cache_component_names);
 
618
 
 
619
        extension->n_cache_directories = n_directories;
458
620
        extension->cache_directories = g_new0 (GFile *, n_directories);
459
 
        extension->trash_directories = g_new0 (GFile *, n_directories);
 
621
        extension->cache_trash_directories = g_new0 (GFile *, n_directories);
460
622
 
461
623
        user_cache_dir = e_get_user_cache_dir ();
462
624
        base_directory = g_file_new_for_path (user_cache_dir);
467
629
                GError *error = NULL;
468
630
 
469
631
                cache_directory = g_file_get_child (
470
 
                        base_directory, component_names[ii]);
 
632
                        base_directory, cache_component_names[ii]);
471
633
                trash_directory = g_file_get_child (
472
634
                        cache_directory, TRASH_DIRECTORY_NAME);
473
635
 
474
636
                /* Cache directory is a parent of the trash
475
637
                 * directory so this is sufficient for both. */
476
 
                g_file_make_directory_with_parents (
 
638
                cache_reaper_make_directory_and_parents (
477
639
                        trash_directory, NULL, &error);
478
640
 
479
 
                if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
480
 
                        g_clear_error (&error);
481
 
 
482
641
                if (error != NULL) {
483
 
                        gchar *path;
484
 
 
485
 
                        path = g_file_get_path (trash_directory);
486
 
                        g_warning (
487
 
                                "Failed to make directory '%s': %s",
488
 
                                path, error->message);
489
 
                        g_free (path);
490
 
 
 
642
                        g_warning ("%s: %s", G_STRFUNC, error->message);
491
643
                        g_error_free (error);
492
644
                }
493
645
 
494
646
                extension->cache_directories[ii] = cache_directory;
495
 
                extension->trash_directories[ii] = trash_directory;
 
647
                extension->cache_trash_directories[ii] = trash_directory;
496
648
        }
497
649
 
498
650
        g_object_unref (base_directory);