Index: gnome-settings-daemon-2.24.0/configure.ac =================================================================== --- gnome-settings-daemon-2.24.0.orig/configure.ac +++ gnome-settings-daemon-2.24.0/configure.ac @@ -215,6 +215,14 @@ PKG_CHECK_MODULES(LIBGNOMEKBD, [libgnome AC_SUBST(LIBGNOMEKBD_CFLAGS) AC_SUBST(LIBGNOMEKBD_LIBS) +dnl --------------------------------------------------------------------------- +dnl - Housekeeping plugin stuff +dnl --------------------------------------------------------------------------- + +PKG_CHECK_MODULES(GIOUNIX, [gio-unix-2.0]) +AC_SUBST(GIOUNIX_CFLAGS) +AC_SUBST(GIOUNIX_LIBS) + dnl ============================================== dnl Esd section dnl ============================================== Index: gnome-settings-daemon-2.24.0/plugins/housekeeping/Makefile.am =================================================================== --- gnome-settings-daemon-2.24.0.orig/plugins/housekeeping/Makefile.am +++ gnome-settings-daemon-2.24.0/plugins/housekeeping/Makefile.am @@ -13,11 +13,13 @@ libhousekeeping_la_CPPFLAGS = \ libhousekeeping_la_CFLAGS = \ $(SETTINGS_PLUGIN_CFLAGS) \ + $(GIOUNIX_CFLAGS) \ + $(LIBNOTIFY_CFLAGS) \ $(AM_CFLAGS) libhousekeeping_la_LDFLAGS = $(GSD_PLUGIN_LDFLAGS) -libhousekeeping_la_LIBADD = $(SETTINGS_PLUGIN_LIBS) +libhousekeeping_la_LIBADD = $(SETTINGS_PLUGIN_LIBS) $(GIOUNIX_LIBS) $(LIBNOTIFY_LIBS) plugin_in_files = housekeeping.gnome-settings-plugin.in Index: gnome-settings-daemon-2.24.0/plugins/housekeeping/gsd-disk-space.c =================================================================== --- /dev/null +++ gnome-settings-daemon-2.24.0/plugins/housekeeping/gsd-disk-space.c @@ -0,0 +1,373 @@ +/* -*- 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 + * + * 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_LIBNOTIFY -DTEST -Wall `pkg-config --cflags --libs gobject-2.0 gio-unix-2.0 glib-2.0 gtk+-2.0 libnotify` -o gsd-disk-space-test gsd-disk-space.c */ + +#include + +#include +#include +#include +#include +#include + +#ifdef HAVE_LIBNOTIFY + +#include + +/* + * TODO: + * + gconf to make it possible to customize the define below (?) + */ + +#define FREE_PERCENT_NOTIFY 0.05 +#define FREE_PERCENT_NOTIFY_AGAIN 0.01 +/* No notification if there's more than 2 GB */ +#define FREE_SIZE_GB_NO_NOTIFY 2 +#define GIGABYTE 1024 * 1024 * 1024 + +#define CHECK_EVERY_X_SECONDS 15 + +#define DISK_SPACE_ANALYZER "baobab" + +static GHashTable *ldsm_notified_hash = NULL; +static unsigned int ldsm_timeout_id = 0; +static GUnixMountMonitor *ldsm_monitor = NULL; + +static void +ldsm_hash_free_slice_gdouble (gpointer data) +{ + g_slice_free (gdouble, data); +} + +static char * +ldsm_get_icon_name_from_g_icon (GIcon *gicon) +{ + const char * const *names; + GtkIconTheme *icon_theme; + int i; + + if (!G_IS_THEMED_ICON (gicon)) + return NULL; + + names = g_themed_icon_get_names (G_THEMED_ICON (gicon)); + icon_theme = gtk_icon_theme_get_default (); + + for (i = 0; names[i] != NULL; i++) { + if (gtk_icon_theme_has_icon (icon_theme, names[i])) + return g_strdup (names[i]); + } + + return NULL; +} + +static void +ldsm_notification_clicked (NotifyNotification *notification, + const char *action, + const char *path) +{ + const char *argv[] = { DISK_SPACE_ANALYZER, path, NULL }; + + if (strcmp (action, "analyze") != 0) { + return; + } + + g_spawn_async (NULL, (char **) argv, NULL, G_SPAWN_SEARCH_PATH, + NULL, NULL, NULL, NULL); +} + +static void +ldsm_notify_for_mount (GUnixMountEntry *mount, + double free_space, + gboolean has_disk_analyzer) +{ + char *name; + char *msg; + GIcon *gicon; + char *icon; + int in_use; + NotifyNotification *notif; + + name = g_unix_mount_guess_name (mount); + in_use = 100 - (free_space * 100); + msg = g_strdup_printf (_("%d%% of the disk space on `%s' is in use"), + in_use, name); + g_free (name); + + gicon = g_unix_mount_guess_icon (mount); + icon = ldsm_get_icon_name_from_g_icon (gicon); + g_object_unref (gicon); + + notif = notify_notification_new (_("Low Disk Space"), msg, icon, NULL); + g_free (msg); + g_free (icon); + + notify_notification_set_urgency (notif, NOTIFY_URGENCY_CRITICAL); + + if (FALSE && has_disk_analyzer) { + const char *path; + + path = g_unix_mount_get_mount_path (mount); + + notify_notification_add_action (notif, "analyze", _("Analyze"), + (NotifyActionCallback) ldsm_notification_clicked, + g_strdup (path), g_free); + } + + notify_notification_show (notif, NULL); + + g_object_unref (notif); +} + +static void +ldsm_check_mount (GUnixMountEntry *mount, + gboolean has_disk_analyzer) +{ + const char *path; + struct statvfs buf; + unsigned long threshold_blocks; + double free_space; + double previous_free_space; + double *previous_free_space_p; + + path = g_unix_mount_get_mount_path (mount); + + /* get the old stats we saved for this mount in case we notified */ + previous_free_space_p = g_hash_table_lookup (ldsm_notified_hash, path); + if (previous_free_space_p != NULL) + previous_free_space = *previous_free_space_p; + else + previous_free_space = 0; + + if (statvfs (path, &buf) != 0) { + g_hash_table_remove (ldsm_notified_hash, path); + return; + } + + /* not a real filesystem, but a virtual one. Skip it */ + if (buf.f_blocks == 0) { + g_hash_table_remove (ldsm_notified_hash, path); + return; + } + + free_space = (double) buf.f_bavail / (double) buf.f_blocks; + /* enough free space, nothing to do */ + if (free_space > FREE_PERCENT_NOTIFY) { + g_hash_table_remove (ldsm_notified_hash, path); + return; + } + + /* note that we try to avoid doing an overflow */ + threshold_blocks = FREE_SIZE_GB_NO_NOTIFY * (GIGABYTE / buf.f_bsize); + /* more than enough space, nothing to do */ + if (buf.f_bavail > threshold_blocks) { + g_hash_table_remove (ldsm_notified_hash, path); + return; + } + + /* did we already notify the user? If yes, we only notify if the disk + * is getting more and more filled */ + if (previous_free_space != 0 && + previous_free_space - free_space < FREE_PERCENT_NOTIFY_AGAIN) { + return; + } + + ldsm_notify_for_mount (mount, free_space, has_disk_analyzer); + + /* replace the information about the latest notification */ + previous_free_space_p = g_slice_new (gdouble); + *previous_free_space_p = free_space; + g_hash_table_replace (ldsm_notified_hash, + g_strdup (path), previous_free_space_p); +} + +static gboolean +ldsm_check_all_mounts (gpointer data) +{ + GList *mounts; + GList *l; + GHashTable *seen; + char *program; + gboolean has_disk_analyzer; + + program = g_find_program_in_path (DISK_SPACE_ANALYZER); + has_disk_analyzer = (program != NULL); + if (program != NULL) { + g_free (program); + } + + /* it's possible to get duplicate mounts, and we don't want duplicate + * notifications */ + seen = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + mounts = g_unix_mounts_get (NULL); + + for (l = mounts; l != NULL; l = l->next) { + GUnixMountEntry *mount = l->data; + const char *path; + + if (g_unix_mount_is_readonly (mount)) { + g_unix_mount_free (mount); + continue; + } + + path = g_unix_mount_get_mount_path (mount); + + if (g_hash_table_lookup_extended (seen, path, NULL, NULL)) { + g_unix_mount_free (mount); + continue; + } + + g_hash_table_insert (seen, + g_strdup (path), GINT_TO_POINTER(1)); + + ldsm_check_mount (mount, has_disk_analyzer); + + g_unix_mount_free (mount); + } + + g_hash_table_destroy (seen); + + 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 (NULL); + 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 void +gsd_ldsm_setup (gboolean check_now) +{ + if (ldsm_notified_hash || ldsm_timeout_id || ldsm_monitor) { + g_critical ("Low disk space monitor already initialized.\n"); + return; + } + + if (!notify_is_initted ()) + if (!notify_init ("Low Disk Space Monitor")) + return; + + ldsm_notified_hash = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, + ldsm_hash_free_slice_gdouble); + + 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); +} + +static void +gsd_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; +} + +#else /* HAVE_LIBNOTIFY */ + +static void +gsd_ldsm_setup (gboolean check_now) +{ +} + +static void +gsd_ldsm_clean (void) +{ +} + +#endif /* HAVE_LIBNOTIFY */ + +#ifdef TEST +int +main (int argc, + char **argv) +{ + GMainLoop *loop; + + gtk_init (&argc, &argv); + + loop = g_main_loop_new (NULL, FALSE); + + gsd_ldsm_setup (TRUE); + + g_main_loop_run (loop); + + gsd_ldsm_clean (); + g_main_loop_unref (loop); + + return 0; +} +#endif /* TEST */ Index: gnome-settings-daemon-2.24.0/plugins/housekeeping/gsd-disk-space.h =================================================================== --- /dev/null +++ gnome-settings-daemon-2.24.0/plugins/housekeeping/gsd-disk-space.h @@ -0,0 +1,36 @@ +/* -*- 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 + * + * 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. + * + */ + +#ifndef __GSD_DISK_SPACE_H +#define __GSD_DISK_SPACE_H + +#include + +G_BEGIN_DECLS + +static void gsd_ldsm_setup (gboolean check_now); +static void gsd_ldsm_clean (void); + +G_END_DECLS + +#endif /* __GSD_DISK_SPACE_H */ Index: gnome-settings-daemon-2.24.0/plugins/housekeeping/gsd-housekeeping-manager.c =================================================================== --- gnome-settings-daemon-2.24.0.orig/plugins/housekeeping/gsd-housekeeping-manager.c +++ gnome-settings-daemon-2.24.0/plugins/housekeeping/gsd-housekeeping-manager.c @@ -302,6 +302,8 @@ gsd_housekeeping_manager_start (GsdHouse g_debug ("Starting housekeeping manager"); gnome_settings_profile_start (NULL); + gsd_ldsm_setup (FALSE); + manager->priv->gconf_notify = register_config_callback (manager, GCONF_THUMB_BINDING_DIR, (GConfClientNotifyFunc) bindings_callback); @@ -352,6 +354,8 @@ gsd_housekeeping_manager_stop (GsdHousek do_cleanup (manager); } } + + gsd_ldsm_clean (); } Index: gnome-settings-daemon-2.24.0/plugins/housekeeping/housekeeping.gnome-settings-plugin.in =================================================================== --- gnome-settings-daemon-2.24.0.orig/plugins/housekeeping/housekeeping.gnome-settings-plugin.in +++ gnome-settings-daemon-2.24.0/plugins/housekeeping/housekeeping.gnome-settings-plugin.in @@ -2,7 +2,7 @@ Module=housekeeping IAge=0 _Name=Housekeeping -_Description=Automatically prunes thumbnail caches and other transient files +_Description=Automatically prunes thumbnail caches and other transient files, and warns about disk usage Authors=Michael J. Chudobiak Copyright=Copyright © 2008 Michael J. Chudobiak Website= Index: gnome-settings-daemon-2.24.0/po/POTFILES.in =================================================================== --- gnome-settings-daemon-2.24.0.orig/po/POTFILES.in +++ gnome-settings-daemon-2.24.0/po/POTFILES.in @@ -16,6 +16,7 @@ plugins/a11y-keyboard/gsd-a11y-preferenc [type: gettext/ini]plugins/dummy/dummy.gnome-settings-plugin.in [type: gettext/ini]plugins/font/font.gnome-settings-plugin.in plugins/font/gsd-font-manager.c +plugins/housekeeping/gsd-disk-space.c plugins/keybindings/gsd-keybindings-manager.c [type: gettext/ini]plugins/keybindings/keybindings.gnome-settings-plugin.in [type: gettext/ini]plugins/keyboard/keyboard.gnome-settings-plugin.in