summaryrefslogtreecommitdiff
path: root/plugins/housekeeping/gsd-disk-space.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/housekeeping/gsd-disk-space.c')
-rw-r--r--plugins/housekeeping/gsd-disk-space.c733
1 files changed, 0 insertions, 733 deletions
diff --git a/plugins/housekeeping/gsd-disk-space.c b/plugins/housekeeping/gsd-disk-space.c
deleted file mode 100644
index 6842ae5..0000000
--- a/plugins/housekeeping/gsd-disk-space.c
+++ /dev/null
@@ -1,733 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
- * vim: set et sw=8 ts=8:
- *
- * Copyright (c) 2008, Novell, Inc.
- *
- * Authors: Vincent Untz <[email protected]>
- *
- * 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.
- *
- */
-
-/* gcc -DHAVE_LIBMATENOTIFY -DTEST -Wall `pkg-config --cflags --libs gobject-2.0 gio-unix-2.0 glib-2.0 gtk+-2.0 libmatenotify` -o msd-disk-space-test msd-disk-space.c */
-
-#include "config.h"
-
-#include <sys/statvfs.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <glib.h>
-#include <glib/gi18n.h>
-#include <glib-object.h>
-#include <gio/gunixmounts.h>
-#include <gio/gio.h>
-#include <gtk/gtk.h>
-#include <mateconf/mateconf-client.h>
-
-#include "msd-disk-space.h"
-#include "msd-ldsm-dialog.h"
-#include "msd-ldsm-trash-empty.h"
-
-
-#define GIGABYTE 1024 * 1024 * 1024
-
-#define CHECK_EVERY_X_SECONDS 60
-
-#define DISK_SPACE_ANALYZER "baobab"
-
-#define MATECONF_HOUSEKEEPING_DIR "/apps/mate_settings_daemon/plugins/housekeeping"
-#define MATECONF_FREE_PC_NOTIFY_KEY "free_percent_notify"
-#define MATECONF_FREE_PC_NOTIFY_AGAIN_KEY "free_percent_notify_again"
-#define MATECONF_FREE_SIZE_NO_NOTIFY "free_size_gb_no_notify"
-#define MATECONF_MIN_NOTIFY_PERIOD "min_notify_period"
-#define MATECONF_IGNORE_PATHS "ignore_paths"
-
-typedef struct
-{
- GUnixMountEntry *mount;
- struct statvfs buf;
- time_t notify_time;
-} LdsmMountInfo;
-
-static GHashTable *ldsm_notified_hash = NULL;
-static unsigned int ldsm_timeout_id = 0;
-static GUnixMountMonitor *ldsm_monitor = NULL;
-static double free_percent_notify = 0.05;
-static double free_percent_notify_again = 0.01;
-static unsigned int free_size_gb_no_notify = 2;
-static unsigned int min_notify_period = 10;
-static GSList *ignore_paths = NULL;
-static unsigned int mateconf_notify_id;
-static MateConfClient *client = NULL;
-static MsdLdsmDialog *dialog = NULL;
-static guint64 *time_read;
-
-static gchar*
-ldsm_get_fs_id_for_path (const gchar *path)
-{
- GFile *file;
- GFileInfo *fileinfo;
- gchar *attr_id_fs;
-
- file = g_file_new_for_path (path);
- fileinfo = g_file_query_info (file, G_FILE_ATTRIBUTE_ID_FILESYSTEM, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, NULL);
- if (fileinfo) {
- attr_id_fs = g_strdup (g_file_info_get_attribute_string (fileinfo, G_FILE_ATTRIBUTE_ID_FILESYSTEM));
- g_object_unref (fileinfo);
- } else {
- attr_id_fs = NULL;
- }
-
- g_object_unref (file);
-
- return attr_id_fs;
-}
-
-static gboolean
-ldsm_mount_has_trash (LdsmMountInfo *mount)
-{
- const gchar *user_data_dir;
- gchar *user_data_attr_id_fs;
- gchar *path_attr_id_fs;
- gboolean mount_uses_user_trash = FALSE;
- gchar *trash_files_dir;
- gboolean has_trash = FALSE;
- GDir *dir;
- const gchar *path;
-
- user_data_dir = g_get_user_data_dir ();
- user_data_attr_id_fs = ldsm_get_fs_id_for_path (user_data_dir);
-
- path = g_unix_mount_get_mount_path (mount->mount);
- path_attr_id_fs = ldsm_get_fs_id_for_path (path);
-
- if (g_strcmp0 (user_data_attr_id_fs, path_attr_id_fs) == 0) {
- /* The volume that is low on space is on the same volume as our home
- * directory. This means the trash is at $XDG_DATA_HOME/Trash,
- * not at the root of the volume which is full.
- */
- mount_uses_user_trash = TRUE;
- }
-
- g_free (user_data_attr_id_fs);
- g_free (path_attr_id_fs);
-
- /* I can't think of a better way to find out if a volume has any trash. Any suggestions? */
- if (mount_uses_user_trash) {
- trash_files_dir = g_build_filename (g_get_user_data_dir (), "Trash", "files", NULL);
- } else {
- gchar *uid;
-
- uid = g_strdup_printf ("%d", getuid ());
- trash_files_dir = g_build_filename (path, ".Trash", uid, "files", NULL);
- if (!g_file_test (trash_files_dir, G_FILE_TEST_IS_DIR)) {
- gchar *trash_dir;
-
- g_free (trash_files_dir);
- trash_dir = g_strdup_printf (".Trash-%s", uid);
- trash_files_dir = g_build_filename (path, trash_dir, "files", NULL);
- g_free (trash_dir);
- if (!g_file_test (trash_files_dir, G_FILE_TEST_IS_DIR)) {
- g_free (trash_files_dir);
- g_free (uid);
- return has_trash;
- }
- }
- g_free (uid);
- }
-
- dir = g_dir_open (trash_files_dir, 0, NULL);
- if (dir) {
- if (g_dir_read_name (dir))
- has_trash = TRUE;
- g_dir_close (dir);
- }
-
- g_free (trash_files_dir);
-
- return has_trash;
-}
-
-static void
-ldsm_analyze_path (const gchar *path)
-{
- const gchar *argv[] = { DISK_SPACE_ANALYZER, path, NULL };
-
- g_spawn_async (NULL, (gchar **) argv, NULL, G_SPAWN_SEARCH_PATH,
- NULL, NULL, NULL, NULL);
-}
-
-static gboolean
-ldsm_notify_for_mount (LdsmMountInfo *mount,
- gboolean multiple_volumes,
- gboolean other_usable_volumes)
-{
- gchar *name, *program;
- gint64 free_space;
- gint response;
- gboolean has_trash;
- gboolean has_disk_analyzer;
- gboolean retval = TRUE;
- const gchar *path;
-
- /* Don't show a dialog if one is already displayed */
- if (dialog)
- return retval;
-
- name = g_unix_mount_guess_name (mount->mount);
- free_space = (gint64) mount->buf.f_frsize * (gint64) mount->buf.f_bavail;
- has_trash = ldsm_mount_has_trash (mount);
- path = g_unix_mount_get_mount_path (mount->mount);
-
- program = g_find_program_in_path (DISK_SPACE_ANALYZER);
- has_disk_analyzer = (program != NULL);
- g_free (program);
-
- dialog = msd_ldsm_dialog_new (other_usable_volumes,
- multiple_volumes,
- has_disk_analyzer,
- has_trash,
- free_space,
- name,
- path);
-
- g_free (name);
-
- g_object_ref (G_OBJECT (dialog));
- response = gtk_dialog_run (GTK_DIALOG (dialog));
-
- gtk_object_destroy (GTK_OBJECT (dialog));
- dialog = NULL;
-
- switch (response) {
- case GTK_RESPONSE_CANCEL:
- retval = FALSE;
- break;
- case MSD_LDSM_DIALOG_RESPONSE_ANALYZE:
- retval = FALSE;
- ldsm_analyze_path (g_unix_mount_get_mount_path (mount->mount));
- break;
- case MSD_LDSM_DIALOG_RESPONSE_EMPTY_TRASH:
- retval = TRUE;
- msd_ldsm_trash_empty ();
- break;
- case GTK_RESPONSE_NONE:
- case GTK_RESPONSE_DELETE_EVENT:
- retval = TRUE;
- break;
- default:
- g_assert_not_reached ();
- }
-
- return retval;
-}
-
-static gboolean
-ldsm_mount_has_space (LdsmMountInfo *mount)
-{
- gdouble free_space;
-
- free_space = (double) mount->buf.f_bavail / (double) mount->buf.f_blocks;
- /* enough free space, nothing to do */
- if (free_space > free_percent_notify)
- return TRUE;
-
- if (((gint64) mount->buf.f_frsize * (gint64) mount->buf.f_bavail) > ((gint64) free_size_gb_no_notify * GIGABYTE))
- return TRUE;
-
- /* If we got here, then this volume is low on space */
- return FALSE;
-}
-
-static gboolean
-ldsm_mount_is_virtual (LdsmMountInfo *mount)
-{
- if (mount->buf.f_blocks == 0) {
- /* Filesystems with zero blocks are virtual */
- return TRUE;
- }
-
- return FALSE;
-}
-
-static gint
-ldsm_ignore_path_compare (gconstpointer a,
- gconstpointer b)
-{
- return g_strcmp0 ((const gchar *)a, (const gchar *)b);
-}
-
-static gboolean
-ldsm_mount_is_user_ignore (const gchar *path)
-{
- if (g_slist_find_custom (ignore_paths, path, (GCompareFunc) ldsm_ignore_path_compare) != NULL)
- return TRUE;
- else
- return FALSE;
-}
-
-
-static gboolean
-is_in (const gchar *value, const gchar *set[])
-{
- int i;
- for (i = 0; set[i] != NULL; i++)
- {
- if (strcmp (set[i], value) == 0)
- return TRUE;
- }
- return FALSE;
-}
-
-static gboolean
-ldsm_mount_should_ignore (GUnixMountEntry *mount)
-{
- const gchar *fs, *device, *path;
-
- path = g_unix_mount_get_mount_path (mount);
- if (ldsm_mount_is_user_ignore (path))
- return TRUE;
-
- /* This is borrowed from GLib and used as a way to determine
- * which mounts we should ignore by default. GLib doesn't
- * expose this in a way that allows it to be used for this
- * purpose
- */
-
- const gchar *ignore_fs[] = {
- "auto",
- "autofs",
- "devfs",
- "devpts",
- "ecryptfs",
- "kernfs",
- "linprocfs",
- "proc",
- "procfs",
- "ptyfs",
- "selinuxfs",
- "linsysfs",
- "sysfs",
- "tmpfs",
- "usbfs",
- "nfsd",
- "rpc_pipefs",
- "zfs",
- NULL
- };
- const gchar *ignore_devices[] = {
- "none",
- "sunrpc",
- "devpts",
- "nfsd",
- "/dev/loop",
- "/dev/vn",
- NULL
- };
-
- fs = g_unix_mount_get_fs_type (mount);
- device = g_unix_mount_get_device_path (mount);
-
- if (is_in (fs, ignore_fs))
- return TRUE;
-
- if (is_in (device, ignore_devices))
- return TRUE;
-
- return FALSE;
-}
-
-static void
-ldsm_free_mount_info (gpointer data)
-{
- LdsmMountInfo *mount = data;
-
- g_return_if_fail (mount != NULL);
-
- g_unix_mount_free (mount->mount);
- g_free (mount);
-}
-
-static void
-ldsm_maybe_warn_mounts (GList *mounts,
- gboolean multiple_volumes,
- gboolean other_usable_volumes)
-{
- GList *l;
- gboolean done = FALSE;
-
- for (l = mounts; l != NULL; l = l->next) {
- LdsmMountInfo *mount_info = l->data;
- LdsmMountInfo *previous_mount_info;
- gdouble free_space;
- gdouble previous_free_space;
- time_t curr_time;
- const gchar *path;
- gboolean show_notify;
-
- if (done) {
- /* Don't show any more dialogs if the user took action with the last one. The user action
- * might free up space on multiple volumes, making the next dialog redundant.
- */
- ldsm_free_mount_info (mount_info);
- continue;
- }
-
- path = g_unix_mount_get_mount_path (mount_info->mount);
-
- previous_mount_info = g_hash_table_lookup (ldsm_notified_hash, path);
- if (previous_mount_info != NULL)
- previous_free_space = (gdouble) previous_mount_info->buf.f_bavail / (gdouble) previous_mount_info->buf.f_blocks;
-
- free_space = (gdouble) mount_info->buf.f_bavail / (gdouble) mount_info->buf.f_blocks;
-
- if (previous_mount_info == NULL) {
- /* We haven't notified for this mount yet */
- show_notify = TRUE;
- mount_info->notify_time = time (NULL);
- g_hash_table_replace (ldsm_notified_hash, g_strdup (path), mount_info);
- } else if ((previous_free_space - free_space) > free_percent_notify_again) {
- /* We've notified for this mount before and free space has decreased sufficiently since last time to notify again */
- curr_time = time (NULL);
- if (difftime (curr_time, previous_mount_info->notify_time) > (gdouble)(min_notify_period * 60)) {
- show_notify = TRUE;
- mount_info->notify_time = curr_time;
- } else {
- /* It's too soon to show the dialog again. However, we still replace the LdsmMountInfo
- * struct in the hash table, but give it the notfiy time from the previous dialog.
- * This will stop the notification from reappearing unnecessarily as soon as the timeout expires.
- */
- show_notify = FALSE;
- mount_info->notify_time = previous_mount_info->notify_time;
- }
- g_hash_table_replace (ldsm_notified_hash, g_strdup (path), mount_info);
- } else {
- /* We've notified for this mount before, but the free space hasn't decreased sufficiently to notify again */
- ldsm_free_mount_info (mount_info);
- show_notify = FALSE;
- }
-
- if (show_notify) {
- if (ldsm_notify_for_mount (mount_info, multiple_volumes, other_usable_volumes))
- done = TRUE;
- }
- }
-}
-
-static gboolean
-ldsm_check_all_mounts (gpointer data)
-{
- GList *mounts;
- GList *l;
- GList *check_mounts = NULL;
- GList *full_mounts = NULL;
- guint number_of_mounts;
- guint number_of_full_mounts;
- gboolean multiple_volumes = FALSE;
- gboolean other_usable_volumes = FALSE;
-
- /* We iterate through the static mounts in /etc/fstab first, seeing if
- * they're mounted by checking if the GUnixMountPoint has a corresponding GUnixMountEntry.
- * Iterating through the static mounts means we automatically ignore dynamically mounted media.
- */
- mounts = g_unix_mount_points_get (time_read);
-
- for (l = mounts; l != NULL; l = l->next) {
- GUnixMountPoint *mount_point = l->data;
- GUnixMountEntry *mount;
- LdsmMountInfo *mount_info;
- const gchar *path;
-
- path = g_unix_mount_point_get_mount_path (mount_point);
- mount = g_unix_mount_at (path, time_read);
- g_unix_mount_point_free (mount_point);
- if (mount == NULL) {
- /* The GUnixMountPoint is not mounted */
- continue;
- }
-
- mount_info = g_new0 (LdsmMountInfo, 1);
- mount_info->mount = mount;
-
- path = g_unix_mount_get_mount_path (mount);
-
- if (g_unix_mount_is_readonly (mount)) {
- ldsm_free_mount_info (mount_info);
- continue;
- }
-
- if (ldsm_mount_should_ignore (mount)) {
- ldsm_free_mount_info (mount_info);
- continue;
- }
-
- if (statvfs (path, &mount_info->buf) != 0) {
- ldsm_free_mount_info (mount_info);
- continue;
- }
-
- if (ldsm_mount_is_virtual (mount_info)) {
- ldsm_free_mount_info (mount_info);
- continue;
- }
-
- check_mounts = g_list_prepend (check_mounts, mount_info);
- }
-
- number_of_mounts = g_list_length (check_mounts);
- if (number_of_mounts > 1)
- multiple_volumes = TRUE;
-
- for (l = check_mounts; l != NULL; l = l->next) {
- LdsmMountInfo *mount_info = l->data;
-
- if (!ldsm_mount_has_space (mount_info)) {
- full_mounts = g_list_prepend (full_mounts, mount_info);
- } else {
- g_hash_table_remove (ldsm_notified_hash, g_unix_mount_get_mount_path (mount_info->mount));
- ldsm_free_mount_info (mount_info);
- }
- }
-
- number_of_full_mounts = g_list_length (full_mounts);
- if (number_of_mounts > number_of_full_mounts)
- other_usable_volumes = TRUE;
-
- ldsm_maybe_warn_mounts (full_mounts, multiple_volumes,
- other_usable_volumes);
-
- g_list_free (check_mounts);
- g_list_free (full_mounts);
-
- return TRUE;
-}
-
-static gboolean
-ldsm_is_hash_item_not_in_mounts (gpointer key,
- gpointer value,
- gpointer user_data)
-{
- GList *l;
-
- for (l = (GList *) user_data; l != NULL; l = l->next) {
- GUnixMountEntry *mount = l->data;
- const char *path;
-
- path = g_unix_mount_get_mount_path (mount);
-
- if (strcmp (path, key) == 0)
- return FALSE;
- }
-
- return TRUE;
-}
-
-static void
-ldsm_mounts_changed (GObject *monitor,
- gpointer data)
-{
- GList *mounts;
-
- /* remove the saved data for mounts that got removed */
- mounts = g_unix_mounts_get (time_read);
- g_hash_table_foreach_remove (ldsm_notified_hash,
- ldsm_is_hash_item_not_in_mounts, mounts);
- g_list_foreach (mounts, (GFunc) g_unix_mount_free, NULL);
-
- /* check the status now, for the new mounts */
- ldsm_check_all_mounts (NULL);
-
- /* and reset the timeout */
- if (ldsm_timeout_id)
- g_source_remove (ldsm_timeout_id);
- ldsm_timeout_id = g_timeout_add_seconds (CHECK_EVERY_X_SECONDS,
- ldsm_check_all_mounts, NULL);
-}
-
-static gboolean
-ldsm_is_hash_item_in_ignore_paths (gpointer key,
- gpointer value,
- gpointer user_data)
-{
- return ldsm_mount_is_user_ignore (key);
-}
-
-static void
-msd_ldsm_get_config ()
-{
- GError *error = NULL;
-
- free_percent_notify = mateconf_client_get_float (client,
- MATECONF_HOUSEKEEPING_DIR "/" MATECONF_FREE_PC_NOTIFY_KEY,
- &error);
- if (error != NULL) {
- g_warning ("Error reading configuration from MateConf: %s", error->message ? error->message : "Unknown error");
- g_clear_error (&error);
- }
- if (free_percent_notify >= 1 || free_percent_notify < 0) {
- g_warning ("Invalid configuration of free_percent_notify: %f\n" \
- "Using sensible default", free_percent_notify);
- free_percent_notify = 0.05;
- }
-
- free_percent_notify_again = mateconf_client_get_float (client,
- MATECONF_HOUSEKEEPING_DIR "/" MATECONF_FREE_PC_NOTIFY_AGAIN_KEY,
- &error);
- if (error != NULL) {
- g_warning ("Error reading configuration from MateConf: %s", error->message ? error->message : "Unknown error");
- g_clear_error (&error);
- }
- if (free_percent_notify_again >= 1 || free_percent_notify_again < 0) {
- g_warning ("Invalid configuration of free_percent_notify_again: %f\n" \
- "Using sensible default\n", free_percent_notify_again);
- free_percent_notify_again = 0.01;
- }
-
- free_size_gb_no_notify = mateconf_client_get_int (client,
- MATECONF_HOUSEKEEPING_DIR "/" MATECONF_FREE_SIZE_NO_NOTIFY,
- &error);
- if (error != NULL) {
- g_warning ("Error reading configuration from MateConf: %s", error->message ? error->message : "Unknown error");
- g_clear_error (&error);
- }
- min_notify_period = mateconf_client_get_int (client,
- MATECONF_HOUSEKEEPING_DIR "/" MATECONF_MIN_NOTIFY_PERIOD,
- &error);
- if (error != NULL) {
- g_warning ("Error reading configuration from MateConf: %s", error->message ? error->message : "Unknown error");
- g_clear_error (&error);
- }
-
- if (ignore_paths != NULL) {
- g_slist_foreach (ignore_paths, (GFunc) g_free, NULL);
- g_slist_free (ignore_paths);
- }
- ignore_paths = mateconf_client_get_list (client,
- MATECONF_HOUSEKEEPING_DIR "/" MATECONF_IGNORE_PATHS,
- MATECONF_VALUE_STRING, &error);
- if (error != NULL) {
- g_warning ("Error reading configuration from MateConf: %s", error->message ? error->message : "Unknown error");
- g_clear_error (&error);
- } else {
- /* Make sure we dont leave stale entries in ldsm_notified_hash */
- g_hash_table_foreach_remove (ldsm_notified_hash,
- ldsm_is_hash_item_in_ignore_paths, NULL);
- }
-}
-
-static void
-msd_ldsm_update_config (MateConfClient *client,
- guint cnxn_id,
- MateConfEntry *entry,
- gpointer user_data)
-{
- msd_ldsm_get_config ();
-}
-
-void
-msd_ldsm_setup (gboolean check_now)
-{
- GError *error = NULL;
-
- if (ldsm_notified_hash || ldsm_timeout_id || ldsm_monitor) {
- g_warning ("Low disk space monitor already initialized.");
- return;
- }
-
- ldsm_notified_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
- g_free,
- ldsm_free_mount_info);
-
- client = mateconf_client_get_default ();
- if (client != NULL) {
- msd_ldsm_get_config ();
- mateconf_notify_id = mateconf_client_notify_add (client,
- MATECONF_HOUSEKEEPING_DIR,
- (MateConfClientNotifyFunc) msd_ldsm_update_config,
- NULL, NULL, &error);
- if (error != NULL) {
- g_warning ("Cannot register callback for MateConf notification");
- g_clear_error (&error);
- }
- } else {
- g_warning ("Failed to get default client");
- }
-
- ldsm_monitor = g_unix_mount_monitor_new ();
- g_unix_mount_monitor_set_rate_limit (ldsm_monitor, 1000);
- g_signal_connect (ldsm_monitor, "mounts-changed",
- G_CALLBACK (ldsm_mounts_changed), NULL);
-
- if (check_now)
- ldsm_check_all_mounts (NULL);
-
- ldsm_timeout_id = g_timeout_add_seconds (CHECK_EVERY_X_SECONDS,
- ldsm_check_all_mounts, NULL);
-
-}
-
-void
-msd_ldsm_clean (void)
-{
- if (ldsm_timeout_id)
- g_source_remove (ldsm_timeout_id);
- ldsm_timeout_id = 0;
-
- if (ldsm_notified_hash)
- g_hash_table_destroy (ldsm_notified_hash);
- ldsm_notified_hash = NULL;
-
- if (ldsm_monitor)
- g_object_unref (ldsm_monitor);
- ldsm_monitor = NULL;
-
- if (client) {
- mateconf_client_notify_remove (client, mateconf_notify_id);
- g_object_unref (client);
- }
-
- if (dialog) {
- gtk_widget_destroy (GTK_WIDGET (dialog));
- dialog = NULL;
- }
-
- if (ignore_paths) {
- g_slist_foreach (ignore_paths, (GFunc) g_free, NULL);
- g_slist_free (ignore_paths);
- }
-}
-
-#ifdef TEST
-int
-main (int argc,
- char **argv)
-{
- GMainLoop *loop;
-
- gtk_init (&argc, &argv);
-
- loop = g_main_loop_new (NULL, FALSE);
-
- msd_ldsm_setup (TRUE);
-
- g_main_loop_run (loop);
-
- msd_ldsm_clean ();
- g_main_loop_unref (loop);
-
- return 0;
-}
-#endif /* TEST */