/* * Copyright (C) 2008 Michael J. Chudobiak * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include "config.h" #include #include #include #include "mate-settings-profile.h" #include "msd-housekeeping-manager.h" #include "msd-disk-space.h" /* General */ #define INTERVAL_ONCE_A_DAY 24*60*60 #define INTERVAL_TWO_MINUTES 2*60 /* Thumbnail cleaner */ #define GSETTINGS_THUMB_SCHEMA "org.mate.thumbnail-cache" #define GSETTINGS_THUMB_AGE "maximum-age" #define DEFAULT_MAX_AGE_IN_DAYS 180 #define GSETTINGS_THUMB_SIZE "maximum-size" #define DEFAULT_MAX_SIZE_IN_MB 512 struct MsdHousekeepingManagerPrivate { guint long_term_cb; guint short_term_cb; GSettings *settings; }; #define MSD_HOUSEKEEPING_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MSD_TYPE_HOUSEKEEPING_MANAGER, MsdHousekeepingManagerPrivate)) static void msd_housekeeping_manager_class_init (MsdHousekeepingManagerClass *klass); static void msd_housekeeping_manager_init (MsdHousekeepingManager *housekeeping_manager); G_DEFINE_TYPE (MsdHousekeepingManager, msd_housekeeping_manager, G_TYPE_OBJECT) static gpointer manager_object = NULL; typedef struct { glong now; glong max_age; goffset total_size; goffset max_size; } PurgeData; typedef struct { time_t mtime; char *path; glong size; } ThumbData; static void thumb_data_free (gpointer data) { ThumbData *info = data; if (info) { g_free (info->path); g_free (info); } } static GList * read_dir_for_purge (const char *path, GList *files) { GFile *read_path; GFileEnumerator *enum_dir; read_path = g_file_new_for_path (path); enum_dir = g_file_enumerate_children (read_path, G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_TIME_MODIFIED "," G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NONE, NULL, NULL); if (enum_dir != NULL) { GFileInfo *info; while ((info = g_file_enumerator_next_file (enum_dir, NULL, NULL)) != NULL) { const char *name; name = g_file_info_get_name (info); if (strlen (name) == 36 && strcmp (name + 32, ".png") == 0) { ThumbData *td; GFile *entry; char *entry_path; GTimeVal mod_time; entry = g_file_get_child (read_path, name); entry_path = g_file_get_path (entry); g_object_unref (entry); g_file_info_get_modification_time (info, &mod_time); td = g_new0 (ThumbData, 1); td->path = entry_path; td->mtime = mod_time.tv_sec; td->size = g_file_info_get_size (info); files = g_list_prepend (files, td); } g_object_unref (info); } g_object_unref (enum_dir); } g_object_unref (read_path); return files; } static void purge_old_thumbnails (ThumbData *info, PurgeData *purge_data) { if ((purge_data->now - info->mtime) > purge_data->max_age) { g_unlink (info->path); info->size = 0; } else { purge_data->total_size += info->size; } } static int sort_file_mtime (ThumbData *file1, ThumbData *file2) { return file1->mtime - file2->mtime; } static int get_gsettings_int_with_default (GSettings *settings, char *key, int default_value) { /* If the key is unset, we use a non-zero default value. A zero value corresponds to an extra-paranoid level of cleaning - it deletes all files. We don't want that as a default condition. */ int value = g_settings_get_int (settings, key); if (value == NULL || value == 0) value = default_value; return value; } static void purge_thumbnail_cache (MsdHousekeepingManager *manager) { char *path; GList *files; PurgeData purge_data; GTimeVal current_time; g_debug ("housekeeping: checking thumbnail cache size and freshness"); path = g_build_filename (g_get_home_dir (), ".thumbnails", "normal", NULL); files = read_dir_for_purge (path, NULL); g_free (path); path = g_build_filename (g_get_home_dir (), ".thumbnails", "large", NULL); files = read_dir_for_purge (path, files); g_free (path); path = g_build_filename (g_get_home_dir (), ".thumbnails", "fail", "mate-thumbnail-factory", NULL); files = read_dir_for_purge (path, files); g_free (path); g_get_current_time (¤t_time); purge_data.now = current_time.tv_sec; purge_data.max_age = get_gsettings_int_with_default (manager->priv->settings, GSETTINGS_THUMB_AGE, DEFAULT_MAX_AGE_IN_DAYS) * 24 * 60 * 60; purge_data.max_size = get_gsettings_int_with_default (manager->priv->settings, GSETTINGS_THUMB_SIZE, DEFAULT_MAX_SIZE_IN_MB) * 1024 * 1024; purge_data.total_size = 0; if (purge_data.max_age >= 0) g_list_foreach (files, (GFunc) purge_old_thumbnails, &purge_data); if ((purge_data.total_size > purge_data.max_size) && (purge_data.max_size >= 0)) { GList *scan; files = g_list_sort (files, (GCompareFunc) sort_file_mtime); for (scan = files; scan && (purge_data.total_size > purge_data.max_size); scan = scan->next) { ThumbData *info = scan->data; g_unlink (info->path); purge_data.total_size -= info->size; } } g_list_foreach (files, (GFunc) thumb_data_free, NULL); g_list_free (files); } static gboolean do_cleanup (MsdHousekeepingManager *manager) { purge_thumbnail_cache (manager); return TRUE; } static gboolean do_cleanup_once (MsdHousekeepingManager *manager) { do_cleanup (manager); manager->priv->short_term_cb = 0; return FALSE; } static void do_cleanup_soon (MsdHousekeepingManager *manager) { if (manager->priv->short_term_cb == 0) { g_debug ("housekeeping: will tidy up in 2 minutes"); manager->priv->short_term_cb = g_timeout_add_seconds (INTERVAL_TWO_MINUTES, (GSourceFunc) do_cleanup_once, manager); } } static void bindings_callback (GSettings *settings, gchar *key, MsdHousekeepingManager *manager) { do_cleanup_soon (manager); } gboolean msd_housekeeping_manager_start (MsdHousekeepingManager *manager, GError **error) { g_debug ("Starting housekeeping manager"); mate_settings_profile_start (NULL); msd_ldsm_setup (FALSE); manager->priv->settings = g_settings_new (GSETTINGS_THUMB_SCHEMA); g_signal_connect (manager->priv->settings, "changed", G_CALLBACK (bindings_callback), manager); /* Clean once, a few minutes after start-up */ do_cleanup_soon (manager); /* Clean periodically, on a daily basis. */ manager->priv->long_term_cb = g_timeout_add_seconds (INTERVAL_ONCE_A_DAY, (GSourceFunc) do_cleanup, manager); mate_settings_profile_end (NULL); return TRUE; } void msd_housekeeping_manager_stop (MsdHousekeepingManager *manager) { MsdHousekeepingManagerPrivate *p = manager->priv; g_debug ("Stopping housekeeping manager"); g_object_unref (p->settings); if (p->short_term_cb) { g_source_remove (p->short_term_cb); p->short_term_cb = 0; } if (p->long_term_cb) { g_source_remove (p->long_term_cb); p->long_term_cb = 0; /* Do a clean-up on shutdown if and only if the size or age limits have been set to paranoid levels (zero) */ if ((get_gsettings_int_with_default (p->settings, GSETTINGS_THUMB_AGE, DEFAULT_MAX_AGE_IN_DAYS) == 0) || (get_gsettings_int_with_default (p->settings, GSETTINGS_THUMB_SIZE, DEFAULT_MAX_SIZE_IN_MB) == 0)) { do_cleanup (manager); } } msd_ldsm_clean (); } static void msd_housekeeping_manager_class_init (MsdHousekeepingManagerClass *klass) { g_type_class_add_private (klass, sizeof (MsdHousekeepingManagerPrivate)); } static void msd_housekeeping_manager_init (MsdHousekeepingManager *manager) { manager->priv = MSD_HOUSEKEEPING_MANAGER_GET_PRIVATE (manager); } MsdHousekeepingManager * msd_housekeeping_manager_new (void) { if (manager_object != NULL) { g_object_ref (manager_object); } else { manager_object = g_object_new (MSD_TYPE_HOUSEKEEPING_MANAGER, NULL); g_object_add_weak_pointer (manager_object, (gpointer *) &manager_object); } return MSD_HOUSEKEEPING_MANAGER (manager_object); }