summaryrefslogtreecommitdiff
path: root/trunk/monitor/hal/ghalvolumemonitor.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/monitor/hal/ghalvolumemonitor.c')
-rw-r--r--trunk/monitor/hal/ghalvolumemonitor.c1360
1 files changed, 1360 insertions, 0 deletions
diff --git a/trunk/monitor/hal/ghalvolumemonitor.c b/trunk/monitor/hal/ghalvolumemonitor.c
new file mode 100644
index 00000000..819cd57b
--- /dev/null
+++ b/trunk/monitor/hal/ghalvolumemonitor.c
@@ -0,0 +1,1360 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz@redhat.com>
+ */
+
+#include <config.h>
+
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <gio/gio.h>
+
+#include "ghalvolumemonitor.h"
+#include "ghalmount.h"
+#include "ghalvolume.h"
+#include "ghaldrive.h"
+#include "hal-pool.h"
+
+/* We use this static variable for enforcing a singleton pattern since
+ * the get_mount_for_mount_path() method on GNativeVolumeMonitor calls
+ * us without an instance.. and ideally we want to piggyback on an
+ * already existing instance.
+ *
+ * We don't need locking since this runs out of process in a single
+ * threaded mode with now weird things happening in signal handlers.
+ */
+
+static GHalVolumeMonitor *the_volume_monitor = NULL;
+static HalPool *pool = NULL;
+
+struct _GHalVolumeMonitor {
+ GNativeVolumeMonitor parent;
+
+ GUnixMountMonitor *mount_monitor;
+
+ HalPool *pool;
+
+ GList *last_optical_disc_devices;
+ GList *last_drive_devices;
+ GList *last_volume_devices;
+ GList *last_mountpoints;
+ GList *last_mounts;
+
+ GList *drives;
+ GList *volumes;
+ GList *mounts;
+
+ /* we keep volumes/mounts for blank and audio discs separate to handle e.g. mixed discs properly */
+ GList *disc_volumes;
+ GList *disc_mounts;
+
+};
+
+static void mountpoints_changed (GUnixMountMonitor *mount_monitor,
+ gpointer user_data);
+static void mounts_changed (GUnixMountMonitor *mount_monitor,
+ gpointer user_data);
+static void hal_changed (HalPool *pool,
+ HalDevice *device,
+ gpointer user_data);
+static void update_all (GHalVolumeMonitor *monitor,
+ gboolean emit_changes,
+ gboolean emit_in_idle);
+static void update_drives (GHalVolumeMonitor *monitor,
+ GList **added_drives,
+ GList **removed_drives);
+static void update_volumes (GHalVolumeMonitor *monitor,
+ GList **added_volumes,
+ GList **removed_volumes);
+static void update_mounts (GHalVolumeMonitor *monitor,
+ GList **added_mounts,
+ GList **removed_mounts);
+static void update_discs (GHalVolumeMonitor *monitor,
+ GList **added_volumes,
+ GList **removed_volumes,
+ GList **added_mounts,
+ GList **removed_mounts);
+
+
+G_DEFINE_TYPE (GHalVolumeMonitor, g_hal_volume_monitor, G_TYPE_NATIVE_VOLUME_MONITOR)
+
+static void
+list_free (GList *objects)
+{
+ g_list_foreach (objects, (GFunc)g_object_unref, NULL);
+ g_list_free (objects);
+}
+
+static HalPool *
+get_hal_pool (void)
+{
+ char *cap_only[] = {"block", NULL};
+
+ if (pool == NULL)
+ pool = hal_pool_new (cap_only);
+
+ return pool;
+}
+
+static void
+g_hal_volume_monitor_dispose (GObject *object)
+{
+ GHalVolumeMonitor *monitor;
+
+ monitor = G_HAL_VOLUME_MONITOR (object);
+
+ the_volume_monitor = NULL;
+
+ if (G_OBJECT_CLASS (g_hal_volume_monitor_parent_class)->dispose)
+ (*G_OBJECT_CLASS (g_hal_volume_monitor_parent_class)->dispose) (object);
+}
+
+static void
+g_hal_volume_monitor_finalize (GObject *object)
+{
+ GHalVolumeMonitor *monitor;
+
+ monitor = G_HAL_VOLUME_MONITOR (object);
+
+ g_signal_handlers_disconnect_by_func (monitor->mount_monitor, mountpoints_changed, monitor);
+ g_signal_handlers_disconnect_by_func (monitor->mount_monitor, mounts_changed, monitor);
+ g_signal_handlers_disconnect_by_func (monitor->pool, hal_changed, monitor);
+
+ g_object_unref (monitor->mount_monitor);
+ g_object_unref (monitor->pool);
+
+ list_free (monitor->last_optical_disc_devices);
+ list_free (monitor->last_drive_devices);
+ list_free (monitor->last_volume_devices);
+ list_free (monitor->last_mountpoints);
+ g_list_foreach (monitor->last_mounts,
+ (GFunc)g_unix_mount_free, NULL);
+ g_list_free (monitor->last_mounts);
+
+ list_free (monitor->drives);
+ list_free (monitor->volumes);
+ list_free (monitor->mounts);
+
+ list_free (monitor->disc_volumes);
+ list_free (monitor->disc_mounts);
+
+ if (G_OBJECT_CLASS (g_hal_volume_monitor_parent_class)->finalize)
+ (*G_OBJECT_CLASS (g_hal_volume_monitor_parent_class)->finalize) (object);
+}
+
+static GList *
+get_mounts (GVolumeMonitor *volume_monitor)
+{
+ GHalVolumeMonitor *monitor;
+ GList *l, *ll;
+
+ monitor = G_HAL_VOLUME_MONITOR (volume_monitor);
+
+ l = g_list_copy (monitor->mounts);
+ ll = g_list_copy (monitor->disc_mounts);
+ l = g_list_concat (l, ll);
+
+ g_list_foreach (l, (GFunc)g_object_ref, NULL);
+
+ return l;
+}
+
+static GList *
+get_volumes (GVolumeMonitor *volume_monitor)
+{
+ GHalVolumeMonitor *monitor;
+ GList *l, *ll;
+
+ monitor = G_HAL_VOLUME_MONITOR (volume_monitor);
+
+ l = g_list_copy (monitor->volumes);
+ ll = g_list_copy (monitor->disc_volumes);
+ l = g_list_concat (l, ll);
+
+ g_list_foreach (l, (GFunc)g_object_ref, NULL);
+
+ return l;
+}
+
+static GList *
+get_connected_drives (GVolumeMonitor *volume_monitor)
+{
+ GHalVolumeMonitor *monitor;
+ GList *l;
+
+ monitor = G_HAL_VOLUME_MONITOR (volume_monitor);
+
+ l = g_list_copy (monitor->drives);
+ g_list_foreach (l, (GFunc)g_object_ref, NULL);
+
+ return l;
+}
+
+static GVolume *
+get_volume_for_uuid (GVolumeMonitor *volume_monitor, const char *uuid)
+{
+ GHalVolumeMonitor *monitor;
+ GHalVolume *volume;
+ GList *l;
+
+ monitor = G_HAL_VOLUME_MONITOR (volume_monitor);
+
+ volume = NULL;
+
+ for (l = monitor->volumes; l != NULL; l = l->next)
+ {
+ volume = l->data;
+ if (g_hal_volume_has_uuid (volume, uuid))
+ goto found;
+ }
+
+ for (l = monitor->disc_volumes; l != NULL; l = l->next)
+ {
+ volume = l->data;
+ if (g_hal_volume_has_uuid (volume, uuid))
+ goto found;
+ }
+
+ return NULL;
+
+ found:
+
+ g_object_ref (volume);
+
+ return (GVolume *)volume;
+}
+
+static GMount *
+get_mount_for_uuid (GVolumeMonitor *volume_monitor, const char *uuid)
+{
+ GHalVolumeMonitor *monitor;
+ GHalMount *mount;
+ GList *l;
+
+ monitor = G_HAL_VOLUME_MONITOR (volume_monitor);
+
+ mount = NULL;
+
+ for (l = monitor->mounts; l != NULL; l = l->next)
+ {
+ mount = l->data;
+ if (g_hal_mount_has_uuid (mount, uuid))
+ goto found;
+ }
+
+ for (l = monitor->disc_mounts; l != NULL; l = l->next)
+ {
+ mount = l->data;
+ if (g_hal_mount_has_uuid (mount, uuid))
+ goto found;
+ }
+
+ return NULL;
+
+ found:
+
+ g_object_ref (mount);
+
+ return (GMount *)mount;
+}
+
+static GMount *
+get_mount_for_mount_path (const char *mount_path,
+ GCancellable *cancellable)
+{
+ GMount *mount;
+ GHalMount *hal_mount;
+ GHalVolumeMonitor *volume_monitor;
+
+ volume_monitor = NULL;
+ if (the_volume_monitor != NULL)
+ volume_monitor = g_object_ref (the_volume_monitor);
+
+ if (volume_monitor == NULL)
+ {
+ /* Dammit, no monitor is set up.. so we have to create one, find
+ * what the user asks for and throw it away again.
+ *
+ * What a waste - especially considering that there's IO
+ * involved in doing this: connect to the system message bus;
+ * IPC to hald...
+ */
+ volume_monitor = G_HAL_VOLUME_MONITOR (g_hal_volume_monitor_new ());
+ }
+
+ mount = NULL;
+
+ /* creation of the volume monitor might actually fail */
+ if (volume_monitor != NULL)
+ {
+ GList *l;
+
+ for (l = volume_monitor->mounts; l != NULL; l = l->next)
+ {
+ hal_mount = l->data;
+
+ if (g_hal_mount_has_mount_path (hal_mount, mount_path))
+ {
+ mount = g_object_ref (hal_mount);
+ break;
+ }
+ }
+
+ g_object_unref (volume_monitor);
+ }
+
+ return (GMount *)mount;
+}
+
+static void
+mountpoints_changed (GUnixMountMonitor *mount_monitor,
+ gpointer user_data)
+{
+ GHalVolumeMonitor *monitor = G_HAL_VOLUME_MONITOR (user_data);
+
+ update_all (monitor, TRUE, TRUE);
+}
+
+static void
+mounts_changed (GUnixMountMonitor *mount_monitor,
+ gpointer user_data)
+{
+ GHalVolumeMonitor *monitor = G_HAL_VOLUME_MONITOR (user_data);
+
+ update_all (monitor, TRUE, TRUE);
+}
+
+void
+g_hal_volume_monitor_force_update (GHalVolumeMonitor *monitor, gboolean emit_in_idle)
+{
+ update_all (monitor, TRUE, emit_in_idle);
+}
+
+static void
+hal_changed (HalPool *pool,
+ HalDevice *device,
+ gpointer user_data)
+{
+ GHalVolumeMonitor *monitor = G_HAL_VOLUME_MONITOR (user_data);
+
+ /*g_warning ("hal changed");*/
+
+ update_all (monitor, TRUE, TRUE);
+}
+
+static GObject *
+g_hal_volume_monitor_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ GObject *object;
+ GHalVolumeMonitor *monitor;
+ GHalVolumeMonitorClass *klass;
+ GObjectClass *parent_class;
+
+ if (the_volume_monitor != NULL)
+ {
+ object = g_object_ref (the_volume_monitor);
+ return object;
+ }
+
+ /*g_warning ("creating hal vm");*/
+
+ object = NULL;
+
+ /* Invoke parent constructor. */
+ klass = G_HAL_VOLUME_MONITOR_CLASS (g_type_class_peek (G_TYPE_HAL_VOLUME_MONITOR));
+ parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
+ object = parent_class->constructor (type,
+ n_construct_properties,
+ construct_properties);
+
+ monitor = G_HAL_VOLUME_MONITOR (object);
+ monitor->pool = g_object_ref (get_hal_pool ());
+
+ monitor->mount_monitor = g_unix_mount_monitor_new ();
+
+ g_signal_connect (monitor->mount_monitor,
+ "mounts_changed", G_CALLBACK (mounts_changed),
+ monitor);
+
+ g_signal_connect (monitor->mount_monitor,
+ "mountpoints_changed", G_CALLBACK (mountpoints_changed),
+ monitor);
+
+ g_signal_connect (monitor->pool,
+ "device_added", G_CALLBACK (hal_changed),
+ monitor);
+
+ g_signal_connect (monitor->pool,
+ "device_removed", G_CALLBACK (hal_changed),
+ monitor);
+
+ update_all (monitor, FALSE, TRUE);
+
+ the_volume_monitor = monitor;
+
+ return object;
+}
+
+static void
+g_hal_volume_monitor_init (GHalVolumeMonitor *monitor)
+{
+}
+
+static gboolean
+is_supported (void)
+{
+ return get_hal_pool() != NULL;
+}
+
+static GVolume *
+adopt_orphan_mount (GMount *mount, GVolumeMonitor *monitor)
+{
+ GList *l;
+ GFile *mount_root;
+ GVolume *ret;
+
+ /* This is called by the union volume monitor which does
+ have a ref to this. So its guaranteed to live, unfortunately
+ the pointer is not passed as an argument :/
+ */
+ ret = NULL;
+
+ if (the_volume_monitor == NULL)
+ {
+ return NULL;
+ }
+
+ mount_root = g_mount_get_root (mount);
+
+ /* cdda:// as foreign mounts */
+ for (l = the_volume_monitor->disc_volumes; l != NULL; l = l->next)
+ {
+ GHalVolume *volume = l->data;
+
+ if (g_hal_volume_has_foreign_mount_root (volume, mount_root))
+ {
+ g_hal_volume_adopt_foreign_mount (volume, mount);
+ ret = g_object_ref (volume);
+ goto found;
+ }
+ }
+
+ found:
+ g_object_unref (mount_root);
+
+ return ret;
+}
+
+static void
+g_hal_volume_monitor_class_init (GHalVolumeMonitorClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GVolumeMonitorClass *monitor_class = G_VOLUME_MONITOR_CLASS (klass);
+ GNativeVolumeMonitorClass *native_class = G_NATIVE_VOLUME_MONITOR_CLASS (klass);
+
+ gobject_class->constructor = g_hal_volume_monitor_constructor;
+ gobject_class->finalize = g_hal_volume_monitor_finalize;
+ gobject_class->dispose = g_hal_volume_monitor_dispose;
+
+ monitor_class->get_mounts = get_mounts;
+ monitor_class->get_volumes = get_volumes;
+ monitor_class->get_connected_drives = get_connected_drives;
+ monitor_class->get_volume_for_uuid = get_volume_for_uuid;
+ monitor_class->get_mount_for_uuid = get_mount_for_uuid;
+ monitor_class->adopt_orphan_mount = adopt_orphan_mount;
+ monitor_class->is_supported = is_supported;
+
+ native_class->get_mount_for_mount_path = get_mount_for_mount_path;
+}
+
+/**
+ * g_hal_volume_monitor_new:
+ *
+ * Returns: a new #GVolumeMonitor.
+ **/
+GVolumeMonitor *
+g_hal_volume_monitor_new (void)
+{
+ GHalVolumeMonitor *monitor;
+
+ monitor = g_object_new (G_TYPE_HAL_VOLUME_MONITOR, NULL);
+
+ return G_VOLUME_MONITOR (monitor);
+}
+
+static void
+diff_sorted_lists (GList *list1,
+ GList *list2,
+ GCompareFunc compare,
+ GList **added,
+ GList **removed)
+{
+ int order;
+
+ *added = *removed = NULL;
+
+ while (list1 != NULL &&
+ list2 != NULL)
+ {
+ order = (*compare) (list1->data, list2->data);
+ if (order < 0)
+ {
+ *removed = g_list_prepend (*removed, list1->data);
+ list1 = list1->next;
+ }
+ else if (order > 0)
+ {
+ *added = g_list_prepend (*added, list2->data);
+ list2 = list2->next;
+ }
+ else
+ { /* same item */
+ list1 = list1->next;
+ list2 = list2->next;
+ }
+ }
+
+ while (list1 != NULL)
+ {
+ *removed = g_list_prepend (*removed, list1->data);
+ list1 = list1->next;
+ }
+ while (list2 != NULL)
+ {
+ *added = g_list_prepend (*added, list2->data);
+ list2 = list2->next;
+ }
+}
+
+static GHalVolume *
+lookup_volume_for_mount_path (GHalVolumeMonitor *monitor,
+ const char *mount_path)
+{
+ GList *l;
+ GHalVolume *found;
+
+ found = NULL;
+
+ for (l = monitor->volumes; l != NULL; l = l->next)
+ {
+ GHalVolume *volume = l->data;
+
+ if (g_hal_volume_has_mount_path (volume, mount_path))
+ {
+ found = volume;
+ break;
+ }
+ }
+
+ return found;
+}
+
+static GHalVolume *
+lookup_volume_for_device_path (GHalVolumeMonitor *monitor,
+ const char *device_path)
+{
+ GList *l;
+
+ for (l = monitor->volumes; l != NULL; l = l->next)
+ {
+ GHalVolume *volume = l->data;
+
+ if (g_hal_volume_has_device_path (volume, device_path))
+ return volume;
+ }
+
+ return NULL;
+}
+
+
+
+static GHalMount *
+find_mount_by_mount_path (GHalVolumeMonitor *monitor,
+ const char *mount_path)
+{
+ GList *l;
+
+ for (l = monitor->mounts; l != NULL; l = l->next)
+ {
+ GHalMount *mount = l->data;
+
+ if (g_hal_mount_has_mount_path (mount, mount_path))
+ return mount;
+ }
+
+ return NULL;
+}
+
+static GHalVolume *
+find_volume_by_udi (GHalVolumeMonitor *monitor, const char *udi)
+{
+ GList *l;
+
+ for (l = monitor->volumes; l != NULL; l = l->next)
+ {
+ GHalVolume *volume = l->data;
+
+ if (g_hal_volume_has_udi (volume, udi))
+ return volume;
+ }
+
+ return NULL;
+}
+
+static GHalDrive *
+find_drive_by_udi (GHalVolumeMonitor *monitor, const char *udi)
+{
+ GList *l;
+
+ for (l = monitor->drives; l != NULL; l = l->next)
+ {
+ GHalDrive *drive = l->data;
+
+ if (g_hal_drive_has_udi (drive, udi))
+ return drive;
+ }
+
+ return NULL;
+}
+
+static GHalMount *
+find_disc_mount_by_udi (GHalVolumeMonitor *monitor, const char *udi)
+{
+ GList *l;
+
+ for (l = monitor->disc_mounts; l != NULL; l = l->next)
+ {
+ GHalMount *mount = l->data;
+
+ if (g_hal_mount_has_udi (mount, udi))
+ return mount;
+ }
+
+ return NULL;
+}
+
+static GHalVolume *
+find_disc_volume_by_udi (GHalVolumeMonitor *monitor, const char *udi)
+{
+ GList *l;
+
+ for (l = monitor->disc_volumes; l != NULL; l = l->next)
+ {
+ GHalVolume *volume = l->data;
+
+ if (g_hal_volume_has_udi (volume, udi))
+ return volume;
+ }
+
+ return NULL;
+}
+
+static gint
+hal_device_compare (HalDevice *a, HalDevice *b)
+{
+ return strcmp (hal_device_get_udi (a), hal_device_get_udi (b));
+}
+
+/* TODO: move to gio */
+static gboolean
+_g_unix_mount_point_guess_should_display (GUnixMountPoint *mount_point)
+{
+ const char *mount_path;
+
+ mount_path = g_unix_mount_point_get_mount_path (mount_point);
+
+ /* Never display internal mountpoints */
+ if (g_unix_is_mount_path_system_internal (mount_path))
+ return FALSE;
+
+ /* Only display things in /media (which are generally user mountable)
+ and home dir (fuse stuff) */
+ if (g_str_has_prefix (mount_path, "/media/"))
+ return TRUE;
+
+ if (g_str_has_prefix (mount_path, g_get_home_dir ()))
+ return TRUE;
+
+ return FALSE;
+}
+
+
+static GUnixMountPoint *
+get_mount_point_for_device (HalDevice *d, GList *fstab_mount_points)
+{
+ GList *l;
+ const char *device_file;
+ const char *device_mount_point;
+
+ device_mount_point = hal_device_get_property_string (d, "volume.mount_point");
+
+ device_file = hal_device_get_property_string (d, "block.device");
+
+ for (l = fstab_mount_points; l != NULL; l = l->next)
+ {
+ GUnixMountPoint *mount_point = l->data;
+ const char *device_path;
+ const char *mount_path;
+
+ mount_path = g_unix_mount_point_get_mount_path (mount_point);
+ if (device_mount_point != NULL &&
+ mount_path != NULL &&
+ strcmp (device_mount_point, mount_path) == 0)
+ return mount_point;
+
+ device_path = g_unix_mount_point_get_device_path (mount_point);
+ if (g_str_has_prefix (device_path, "LABEL="))
+ {
+ if (strcmp (device_path + 6, hal_device_get_property_string (d, "volume.label")) == 0)
+ return mount_point;
+ }
+ else if (g_str_has_prefix (device_path, "UUID="))
+ {
+ if (g_ascii_strcasecmp (device_path + 5, hal_device_get_property_string (d, "volume.uuid")) == 0)
+ return mount_point;
+ }
+ else
+ {
+ char resolved_device_path[PATH_MAX];
+ /* handle symlinks such as /dev/disk/by-uuid/47C2-1994 */
+ if (realpath (device_path, resolved_device_path) != NULL &&
+ strcmp (resolved_device_path, device_file) == 0)
+ return mount_point;
+ }
+ }
+
+ return NULL;
+}
+
+static gboolean
+should_mount_be_ignored (HalPool *pool, HalDevice *d)
+{
+ const char *device_mount_point;
+
+ device_mount_point = hal_device_get_property_string (d, "volume.mount_point");
+ if (device_mount_point != NULL && strlen (device_mount_point) > 0)
+ {
+ GUnixMountEntry *mount_entry;
+
+ /*g_warning ("device_mount_point = '%s'", device_mount_point);*/
+
+ mount_entry = g_unix_mount_at (device_mount_point, NULL);
+ if (mount_entry != NULL) {
+ if (!g_unix_mount_guess_should_display (mount_entry))
+ {
+ g_unix_mount_free (mount_entry);
+ return TRUE;
+ }
+ g_unix_mount_free (mount_entry);
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+should_volume_be_ignored (HalPool *pool, HalDevice *d, GList *fstab_mount_points)
+{
+ gboolean volume_ignore;
+ const char *volume_fsusage;
+ GUnixMountPoint *mount_point;
+
+ volume_fsusage = hal_device_get_property_string (d, "volume.fsusage");
+ volume_ignore = hal_device_get_property_bool (d, "volume.ignore");
+
+ if (volume_fsusage == NULL)
+ {
+ /*g_warning ("no volume.fsusage property. Refusing to ignore");*/
+ return FALSE;
+ }
+
+ if (volume_ignore)
+ return TRUE;
+
+ if (strcmp (volume_fsusage, "filesystem") != 0)
+ {
+ /* no file system on the volume... blank and audio discs are handled in update_discs() */
+
+ /* check if it's a LUKS crypto volume */
+ if (strcmp (volume_fsusage, "crypto") == 0)
+ {
+ if (strcmp (hal_device_get_property_string (d, "volume.fstype"), "crypto_LUKS") == 0)
+ {
+ HalDevice *cleartext_device;
+
+ /* avoid showing cryptotext volume if it's corresponding cleartext volume is available */
+ cleartext_device = hal_pool_get_device_by_capability_and_string (pool,
+ "block",
+ "volume.crypto_luks.clear.backing_volume",
+ hal_device_get_udi (d));
+
+ if (cleartext_device == NULL)
+ {
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+ }
+
+ mount_point = get_mount_point_for_device (d, fstab_mount_points);
+ if (mount_point != NULL && !_g_unix_mount_point_guess_should_display (mount_point))
+ return TRUE;
+
+ if (hal_device_get_property_bool (d, "volume.is_mounted"))
+ return should_mount_be_ignored (pool, d);
+
+ return FALSE;
+}
+
+static gboolean
+should_drive_be_ignored (HalPool *pool, HalDevice *d, GList *fstab_mount_points)
+{
+ GList *volumes, *l;
+ const char *drive_udi;
+ gboolean all_volumes_ignored, got_volumes;
+
+ /* never ignore drives with removable media */
+ if (hal_device_get_property_bool (d, "storage.removable"))
+ return FALSE;
+
+ drive_udi = hal_device_get_udi (d);
+
+ volumes = hal_pool_find_by_capability (pool, "volume");
+
+ all_volumes_ignored = TRUE;
+ got_volumes = FALSE;
+ for (l = volumes; l != NULL; l = l->next)
+ {
+ HalDevice *volume_dev = l->data;
+ if (strcmp (drive_udi, hal_device_get_property_string (volume_dev, "block.storage_device")) == 0)
+ {
+ got_volumes = TRUE;
+ if (!should_volume_be_ignored (pool, volume_dev, fstab_mount_points) ||
+ hal_device_get_property_bool (volume_dev, "volume.disc.has_audio") ||
+ hal_device_get_property_bool (volume_dev, "volume.disc.is_blank"))
+ {
+ all_volumes_ignored = FALSE;
+ break;
+ }
+ }
+ }
+
+ return got_volumes && all_volumes_ignored;
+}
+
+static void
+list_emit (GHalVolumeMonitor *monitor,
+ const char *monitor_signal,
+ const char *object_signal,
+ GList *objects)
+{
+ GList *l;
+
+ for (l = objects; l != NULL; l = l->next)
+ {
+ g_signal_emit_by_name (monitor, monitor_signal, l->data);
+ if (object_signal)
+ g_signal_emit_by_name (l->data, object_signal);
+ }
+}
+
+typedef struct {
+ GHalVolumeMonitor *monitor;
+ GList *added_drives, *removed_drives;
+ GList *added_volumes, *removed_volumes;
+ GList *added_mounts, *removed_mounts;
+} ChangedLists;
+
+
+static gboolean
+emit_lists_in_idle (gpointer data)
+{
+ ChangedLists *lists = data;
+
+ list_emit (lists->monitor,
+ "drive_disconnected", NULL,
+ lists->removed_drives);
+ list_emit (lists->monitor,
+ "drive_connected", NULL,
+ lists->added_drives);
+
+ list_emit (lists->monitor,
+ "volume_removed", "removed",
+ lists->removed_volumes);
+ list_emit (lists->monitor,
+ "volume_added", NULL,
+ lists->added_volumes);
+
+ list_emit (lists->monitor,
+ "mount_removed", "unmounted",
+ lists->removed_mounts);
+ list_emit (lists->monitor,
+ "mount_added", NULL,
+ lists->added_mounts);
+
+ list_free (lists->removed_drives);
+ list_free (lists->added_drives);
+ list_free (lists->removed_volumes);
+ list_free (lists->added_volumes);
+ list_free (lists->removed_mounts);
+ list_free (lists->added_mounts);
+ g_object_unref (lists->monitor);
+ g_free (lists);
+
+ return FALSE;
+}
+
+/* Must be called from idle if emit_changes, with no locks held */
+static void
+update_all (GHalVolumeMonitor *monitor,
+ gboolean emit_changes,
+ gboolean emit_in_idle)
+{
+ ChangedLists *lists;
+ GList *added_drives, *removed_drives;
+ GList *added_volumes, *removed_volumes;
+ GList *added_mounts, *removed_mounts;
+
+ added_drives = NULL;
+ removed_drives = NULL;
+ added_volumes = NULL;
+ removed_volumes = NULL;
+ added_mounts = NULL;
+ removed_mounts = NULL;
+
+ update_drives (monitor, &added_drives, &removed_drives);
+ update_volumes (monitor, &added_volumes, &removed_volumes);
+ update_mounts (monitor, &added_mounts, &removed_mounts);
+ update_discs (monitor,
+ &added_volumes, &removed_volumes,
+ &added_mounts, &removed_mounts);
+
+ if (emit_changes)
+ {
+ lists = g_new0 (ChangedLists, 1);
+ lists->monitor = g_object_ref (monitor);
+ lists->added_drives = added_drives;
+ lists->removed_drives = removed_drives;
+ lists->added_volumes = added_volumes;
+ lists->removed_volumes = removed_volumes;
+ lists->added_mounts = added_mounts;
+ lists->removed_mounts = removed_mounts;
+
+ if (emit_in_idle)
+ g_idle_add (emit_lists_in_idle, lists);
+ else
+ emit_lists_in_idle (lists);
+ }
+ else
+ {
+ list_free (removed_drives);
+ list_free (added_drives);
+ list_free (removed_volumes);
+ list_free (added_volumes);
+ list_free (removed_mounts);
+ list_free (added_mounts);
+ }
+}
+
+static void
+update_drives (GHalVolumeMonitor *monitor,
+ GList **added_drives,
+ GList **removed_drives)
+{
+ GList *new_drive_devices;
+ GList *removed, *added;
+ GList *l, *ll;
+ GHalDrive *drive;
+ GList *fstab_mount_points;
+
+ fstab_mount_points = g_unix_mount_points_get (NULL);
+
+ new_drive_devices = hal_pool_find_by_capability (monitor->pool, "storage");
+
+ /* remove devices we want to ignore - we do it here so we get to reevaluate
+ * on the next update whether they should still be ignored
+ */
+ for (l = new_drive_devices; l != NULL; l = ll)
+ {
+ HalDevice *d = l->data;
+ ll = l->next;
+ if (should_drive_be_ignored (monitor->pool, d, fstab_mount_points))
+ new_drive_devices = g_list_delete_link (new_drive_devices, l);
+ }
+
+ g_list_foreach (new_drive_devices, (GFunc) g_object_ref, NULL);
+
+ new_drive_devices = g_list_sort (new_drive_devices, (GCompareFunc) hal_device_compare);
+ diff_sorted_lists (monitor->last_drive_devices,
+ new_drive_devices, (GCompareFunc) hal_device_compare,
+ &added, &removed);
+
+ for (l = removed; l != NULL; l = l->next)
+ {
+ HalDevice *d = l->data;
+
+ drive = find_drive_by_udi (monitor, hal_device_get_udi (d));
+ if (drive != NULL)
+ {
+ /*g_warning ("hal removing drive %s", hal_device_get_property_string (d, "block.device"));*/
+ g_hal_drive_disconnected (drive);
+ monitor->drives = g_list_remove (monitor->drives, drive);
+ *removed_drives = g_list_prepend (*removed_drives, drive);
+ }
+ }
+
+ for (l = added; l != NULL; l = l->next)
+ {
+ HalDevice *d = l->data;
+
+ drive = find_drive_by_udi (monitor, hal_device_get_udi (d));
+ if (drive == NULL)
+ {
+ /*g_warning ("hal adding drive %s", hal_device_get_property_string (d, "block.device"));*/
+ drive = g_hal_drive_new (G_VOLUME_MONITOR (monitor), d, monitor->pool);
+ if (drive != NULL)
+ {
+ monitor->drives = g_list_prepend (monitor->drives, drive);
+ *added_drives = g_list_prepend (*added_drives, g_object_ref (drive));
+ }
+ }
+ }
+
+ g_list_free (added);
+ g_list_free (removed);
+ list_free (monitor->last_drive_devices);
+ monitor->last_drive_devices = new_drive_devices;
+
+ g_list_foreach (fstab_mount_points, (GFunc) g_unix_mount_point_free, NULL);
+ g_list_free (fstab_mount_points);
+}
+
+static void
+update_volumes (GHalVolumeMonitor *monitor,
+ GList **added_volumes,
+ GList **removed_volumes)
+{
+ GList *new_volume_devices;
+ GList *removed, *added;
+ GList *l, *ll;
+ GHalVolume *volume;
+ GHalDrive *drive;
+ GList *fstab_mount_points;
+
+ fstab_mount_points = g_unix_mount_points_get (NULL);
+
+ new_volume_devices = hal_pool_find_by_capability (monitor->pool, "volume");
+
+ /* remove devices we want to ignore - we do it here so we get to reevaluate
+ * on the next update whether they should still be ignored
+ */
+ for (l = new_volume_devices; l != NULL; l = ll)
+ {
+ HalDevice *d = l->data;
+ ll = l->next;
+ if (should_volume_be_ignored (monitor->pool, d, fstab_mount_points))
+ new_volume_devices = g_list_delete_link (new_volume_devices, l);
+ }
+
+ g_list_foreach (new_volume_devices, (GFunc) g_object_ref, NULL);
+
+ new_volume_devices = g_list_sort (new_volume_devices, (GCompareFunc) hal_device_compare);
+ diff_sorted_lists (monitor->last_volume_devices,
+ new_volume_devices, (GCompareFunc) hal_device_compare,
+ &added, &removed);
+
+ for (l = removed; l != NULL; l = l->next)
+ {
+ HalDevice *d = l->data;
+
+ volume = find_volume_by_udi (monitor, hal_device_get_udi (d));
+ if (volume != NULL)
+ {
+ /*g_warning ("hal removing vol %s", hal_device_get_property_string (d, "block.device"));*/
+ g_hal_volume_removed (volume);
+ monitor->volumes = g_list_remove (monitor->volumes, volume);
+
+ *removed_volumes = g_list_prepend (*removed_volumes, volume);
+ }
+ }
+
+ for (l = added; l != NULL; l = l->next)
+ {
+ HalDevice *d = l->data;
+
+ volume = find_volume_by_udi (monitor, hal_device_get_udi (d));
+ if (volume == NULL)
+ {
+ drive = find_drive_by_udi (monitor, hal_device_get_property_string (d, "block.storage_device"));
+
+ /*g_warning ("hal adding vol %s (drive %p)", hal_device_get_property_string (d, "block.device"), drive);*/
+ volume = g_hal_volume_new (G_VOLUME_MONITOR (monitor),
+ d,
+ monitor->pool,
+ NULL,
+ TRUE,
+ drive);
+ if (volume != NULL)
+ {
+ monitor->volumes = g_list_prepend (monitor->volumes, volume);
+ *added_volumes = g_list_prepend (*added_volumes, g_object_ref (volume));
+ }
+ }
+ }
+
+ g_list_free (added);
+ g_list_free (removed);
+ list_free (monitor->last_volume_devices);
+ monitor->last_volume_devices = new_volume_devices;
+
+ g_list_foreach (fstab_mount_points, (GFunc) g_unix_mount_point_free, NULL);
+ g_list_free (fstab_mount_points);
+}
+
+static void
+update_mounts (GHalVolumeMonitor *monitor,
+ GList **added_mounts,
+ GList **removed_mounts)
+{
+ GList *new_mounts;
+ GList *removed, *added;
+ GList *l, *ll;
+ GHalMount *mount;
+ GHalVolume *volume;
+ const char *device_path;
+ const char *mount_path;
+
+ new_mounts = g_unix_mounts_get (NULL);
+
+ /* remove mounts we want to ignore - we do it here so we get to reevaluate
+ * on the next update whether they should still be ignored
+ */
+ for (l = new_mounts; l != NULL; l = ll)
+ {
+ GUnixMountEntry *mount_entry = l->data;
+ ll = l->next;
+
+ /* keep in sync with should_mount_be_ignored() */
+ if (!g_unix_mount_guess_should_display (mount_entry))
+ {
+ g_unix_mount_free (mount_entry);
+ new_mounts = g_list_delete_link (new_mounts, l);
+ }
+ }
+
+ new_mounts = g_list_sort (new_mounts, (GCompareFunc) g_unix_mount_compare);
+
+ diff_sorted_lists (monitor->last_mounts,
+ new_mounts, (GCompareFunc) g_unix_mount_compare,
+ &added, &removed);
+
+ for (l = removed; l != NULL; l = l->next)
+ {
+ GUnixMountEntry *mount_entry = l->data;
+
+ mount = find_mount_by_mount_path (monitor, g_unix_mount_get_mount_path (mount_entry));
+ /*g_warning ("hal removing mount %s (%p)", g_unix_mount_get_device_path (mount_entry), mount);*/
+ if (mount)
+ {
+ g_hal_mount_unmounted (mount);
+ monitor->mounts = g_list_remove (monitor->mounts, mount);
+
+ *removed_mounts = g_list_prepend (*removed_mounts, mount);
+ }
+ }
+
+ for (l = added; l != NULL; l = l->next)
+ {
+ GUnixMountEntry *mount_entry = l->data;
+
+ device_path = g_unix_mount_get_device_path (mount_entry);
+ mount_path = g_unix_mount_get_mount_path (mount_entry);
+ volume = lookup_volume_for_device_path (monitor, device_path);
+ if (volume == NULL)
+ volume = lookup_volume_for_mount_path (monitor, mount_path);
+
+ /*g_warning ("hal adding mount %s (vol %p)", g_unix_mount_get_device_path (mount_entry), volume);*/
+ mount = g_hal_mount_new (G_VOLUME_MONITOR (monitor), mount_entry, monitor->pool, volume);
+ if (mount)
+ {
+ monitor->mounts = g_list_prepend (monitor->mounts, mount);
+ *added_mounts = g_list_prepend (*added_mounts, g_object_ref (mount));
+ }
+ }
+
+ g_list_free (added);
+ g_list_free (removed);
+ g_list_foreach (monitor->last_mounts,
+ (GFunc)g_unix_mount_free, NULL);
+ g_list_free (monitor->last_mounts);
+ monitor->last_mounts = new_mounts;
+}
+
+static void
+update_discs (GHalVolumeMonitor *monitor,
+ GList **added_volumes,
+ GList **removed_volumes,
+ GList **added_mounts,
+ GList **removed_mounts)
+{
+ GList *new_optical_disc_devices;
+ GList *removed, *added;
+ GList *l, *ll;
+ GHalDrive *drive;
+ GHalVolume *volume;
+ GHalMount *mount;
+ const char *udi;
+ const char *drive_udi;
+
+ /* we also need to generate GVolume + GMount objects for
+ *
+ * - optical discs that have audio
+ * - optical discs that are blank
+ *
+ */
+
+ new_optical_disc_devices = hal_pool_find_by_capability (monitor->pool, "volume.disc");
+ for (l = new_optical_disc_devices; l != NULL; l = ll)
+ {
+ HalDevice *d = l->data;
+ ll = l->next;
+ if (! (hal_device_get_property_bool (d, "volume.disc.is_blank") ||
+ hal_device_get_property_bool (d, "volume.disc.has_audio")))
+ {
+ /* filter out everything but discs that are blank or has audio */
+ new_optical_disc_devices = g_list_delete_link (new_optical_disc_devices, l);
+ }
+ }
+
+ g_list_foreach (new_optical_disc_devices, (GFunc) g_object_ref, NULL);
+
+ new_optical_disc_devices = g_list_sort (new_optical_disc_devices, (GCompareFunc) hal_device_compare);
+ diff_sorted_lists (monitor->last_optical_disc_devices,
+ new_optical_disc_devices, (GCompareFunc) hal_device_compare,
+ &added, &removed);
+
+ for (l = removed; l != NULL; l = l->next)
+ {
+ HalDevice *d = l->data;
+
+ udi = hal_device_get_udi (d);
+ /*g_warning ("audio/blank disc removing %s", udi);*/
+
+ mount = find_disc_mount_by_udi (monitor, udi);
+ if (mount != NULL)
+ {
+ g_hal_mount_unmounted (mount);
+ monitor->disc_mounts = g_list_remove (monitor->disc_mounts, mount);
+ *removed_mounts = g_list_prepend (*removed_mounts, mount);
+ }
+
+ volume = find_disc_volume_by_udi (monitor, udi);
+ if (volume != NULL)
+ {
+ g_hal_volume_removed (volume);
+ monitor->disc_volumes = g_list_remove (monitor->disc_volumes, volume);
+ *removed_volumes = g_list_prepend (*removed_volumes, volume);
+ }
+ }
+
+ for (l = added; l != NULL; l = l->next)
+ {
+ HalDevice *d = l->data;
+
+ udi = hal_device_get_udi (d);
+ /*g_warning ("audio/blank disc adding %s", udi);*/
+
+ drive_udi = hal_device_get_property_string (d, "block.storage_device");
+ drive = find_drive_by_udi (monitor, drive_udi);
+ if (drive != NULL)
+ {
+ mount = NULL;
+ if (hal_device_get_property_bool (d, "volume.disc.is_blank"))
+ {
+ volume = g_hal_volume_new (G_VOLUME_MONITOR (monitor),
+ d, monitor->pool,
+ NULL,
+ FALSE,
+ drive);
+ if (volume != NULL)
+ {
+ GFile *root;
+ root = g_file_new_for_uri ("burn:///");
+ mount = g_hal_mount_new_for_hal_device (G_VOLUME_MONITOR (monitor),
+ d,
+ root,
+ NULL,
+ NULL,
+ TRUE,
+ monitor->pool,
+ volume);
+ g_object_unref (root);
+ }
+ }
+ else
+ {
+ char *uri;
+ char *device_basename;
+ GFile *foreign_mount_root;
+
+ /* the gvfsd-cdda backend uses URI's like these */
+ device_basename = g_path_get_basename (hal_device_get_property_string (d, "block.device"));
+ uri = g_strdup_printf ("cdda://%s", device_basename);
+ foreign_mount_root = g_file_new_for_uri (uri);
+ g_free (device_basename);
+ g_free (uri);
+
+ volume = g_hal_volume_new (G_VOLUME_MONITOR (monitor),
+ d,
+ monitor->pool,
+ foreign_mount_root,
+ TRUE,
+ drive);
+ g_object_unref (foreign_mount_root);
+ mount = NULL;
+ }
+
+ if (volume != NULL)
+ {
+ monitor->disc_volumes = g_list_prepend (monitor->disc_volumes, volume);
+ *added_volumes = g_list_prepend (*added_volumes, g_object_ref (volume));
+
+ if (mount != NULL)
+ {
+ monitor->disc_mounts = g_list_prepend (monitor->disc_mounts, mount);
+ *added_mounts = g_list_prepend (*added_mounts, g_object_ref (mount));
+ }
+ }
+ }
+ }
+
+ g_list_free (added);
+ g_list_free (removed);
+ list_free (monitor->last_optical_disc_devices);
+ monitor->last_optical_disc_devices = new_optical_disc_devices;
+}