Home | History | Annotate | Download | only in gio
      1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
      2 
      3 /* GIO - GLib Input, Output and Streaming Library
      4  *
      5  * Copyright (C) 2006-2007 Red Hat, Inc.
      6  *
      7  * This library is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU Lesser General Public
      9  * License as published by the Free Software Foundation; either
     10  * version 2 of the License, or (at your option) any later version.
     11  *
     12  * This library is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  * Lesser General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU Lesser General
     18  * Public License along with this library; if not, write to the
     19  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
     20  * Boston, MA 02111-1307, USA.
     21  *
     22  * Author: Alexander Larsson <alexl (at) redhat.com>
     23  *         David Zeuthen <davidz (at) redhat.com>
     24  */
     25 
     26 #include "config.h"
     27 
     28 #include <string.h>
     29 
     30 #include <glib.h>
     31 #include "gunixvolumemonitor.h"
     32 #include "gunixmounts.h"
     33 #include "gunixmount.h"
     34 #include "gunixvolume.h"
     35 #include "gmount.h"
     36 #include "gmountprivate.h"
     37 #include "giomodule.h"
     38 #include "glibintl.h"
     39 
     40 #include "gioalias.h"
     41 
     42 struct _GUnixVolumeMonitor {
     43   GNativeVolumeMonitor parent;
     44 
     45   GUnixMountMonitor *mount_monitor;
     46 
     47   GList *last_mountpoints;
     48   GList *last_mounts;
     49 
     50   GList *volumes;
     51   GList *mounts;
     52 };
     53 
     54 static void mountpoints_changed      (GUnixMountMonitor  *mount_monitor,
     55                                       gpointer            user_data);
     56 static void mounts_changed           (GUnixMountMonitor  *mount_monitor,
     57                                       gpointer            user_data);
     58 static void update_volumes           (GUnixVolumeMonitor *monitor);
     59 static void update_mounts            (GUnixVolumeMonitor *monitor);
     60 
     61 #define g_unix_volume_monitor_get_type _g_unix_volume_monitor_get_type
     62 G_DEFINE_TYPE_WITH_CODE (GUnixVolumeMonitor, g_unix_volume_monitor, G_TYPE_NATIVE_VOLUME_MONITOR,
     63                          g_io_extension_point_implement (G_NATIVE_VOLUME_MONITOR_EXTENSION_POINT_NAME,
     64 							 g_define_type_id,
     65 							 "unix",
     66 							 0));
     67 
     68 static void
     69 g_unix_volume_monitor_finalize (GObject *object)
     70 {
     71   GUnixVolumeMonitor *monitor;
     72 
     73   monitor = G_UNIX_VOLUME_MONITOR (object);
     74 
     75   g_signal_handlers_disconnect_by_func (monitor->mount_monitor, mountpoints_changed, monitor);
     76   g_signal_handlers_disconnect_by_func (monitor->mount_monitor, mounts_changed, monitor);
     77 
     78   g_object_unref (monitor->mount_monitor);
     79 
     80   g_list_foreach (monitor->last_mountpoints, (GFunc)g_unix_mount_point_free, NULL);
     81   g_list_free (monitor->last_mountpoints);
     82   g_list_foreach (monitor->last_mounts, (GFunc)g_unix_mount_free, NULL);
     83   g_list_free (monitor->last_mounts);
     84 
     85   g_list_foreach (monitor->volumes, (GFunc)g_object_unref, NULL);
     86   g_list_free (monitor->volumes);
     87   g_list_foreach (monitor->mounts, (GFunc)g_object_unref, NULL);
     88   g_list_free (monitor->mounts);
     89 
     90   G_OBJECT_CLASS (g_unix_volume_monitor_parent_class)->finalize (object);
     91 }
     92 
     93 static void
     94 g_unix_volume_monitor_dispose (GObject *object)
     95 {
     96   GUnixVolumeMonitor *monitor;
     97 
     98   monitor = G_UNIX_VOLUME_MONITOR (object);
     99   g_list_foreach (monitor->volumes, (GFunc)g_object_unref, NULL);
    100   g_list_free (monitor->volumes);
    101   monitor->volumes = NULL;
    102 
    103   g_list_foreach (monitor->mounts, (GFunc)g_object_unref, NULL);
    104   g_list_free (monitor->mounts);
    105   monitor->mounts = NULL;
    106 
    107   G_OBJECT_CLASS (g_unix_volume_monitor_parent_class)->dispose (object);
    108 }
    109 
    110 static GList *
    111 get_mounts (GVolumeMonitor *volume_monitor)
    112 {
    113   GUnixVolumeMonitor *monitor;
    114   GList *l;
    115 
    116   monitor = G_UNIX_VOLUME_MONITOR (volume_monitor);
    117 
    118   l = g_list_copy (monitor->mounts);
    119   g_list_foreach (l, (GFunc)g_object_ref, NULL);
    120 
    121   return l;
    122 }
    123 
    124 static GList *
    125 get_volumes (GVolumeMonitor *volume_monitor)
    126 {
    127   GUnixVolumeMonitor *monitor;
    128   GList *l;
    129 
    130   monitor = G_UNIX_VOLUME_MONITOR (volume_monitor);
    131 
    132   l = g_list_copy (monitor->volumes);
    133   g_list_foreach (l, (GFunc)g_object_ref, NULL);
    134 
    135   return l;
    136 }
    137 
    138 static GList *
    139 get_connected_drives (GVolumeMonitor *volume_monitor)
    140 {
    141   return NULL;
    142 }
    143 
    144 static GVolume *
    145 get_volume_for_uuid (GVolumeMonitor *volume_monitor, const char *uuid)
    146 {
    147   return NULL;
    148 }
    149 
    150 static GMount *
    151 get_mount_for_uuid (GVolumeMonitor *volume_monitor, const char *uuid)
    152 {
    153   return NULL;
    154 }
    155 
    156 static gboolean
    157 is_supported (void)
    158 {
    159   return TRUE;
    160 }
    161 
    162 static GMount *
    163 get_mount_for_mount_path (const char *mount_path,
    164                           GCancellable *cancellable)
    165 {
    166   GUnixMountEntry *mount_entry;
    167   GUnixMount *mount;
    168 
    169   mount_entry = g_unix_mount_at (mount_path, NULL);
    170 
    171   if (!mount_entry)
    172     return NULL;
    173 
    174   /* TODO: Set mountable volume? */
    175   mount = _g_unix_mount_new (NULL, mount_entry, NULL);
    176 
    177   g_unix_mount_free (mount_entry);
    178 
    179   return G_MOUNT (mount);
    180 }
    181 
    182 static void
    183 g_unix_volume_monitor_class_init (GUnixVolumeMonitorClass *klass)
    184 {
    185   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
    186   GVolumeMonitorClass *monitor_class = G_VOLUME_MONITOR_CLASS (klass);
    187   GNativeVolumeMonitorClass *native_class = G_NATIVE_VOLUME_MONITOR_CLASS (klass);
    188 
    189   gobject_class->finalize = g_unix_volume_monitor_finalize;
    190   gobject_class->dispose = g_unix_volume_monitor_dispose;
    191 
    192   monitor_class->get_mounts = get_mounts;
    193   monitor_class->get_volumes = get_volumes;
    194   monitor_class->get_connected_drives = get_connected_drives;
    195   monitor_class->get_volume_for_uuid = get_volume_for_uuid;
    196   monitor_class->get_mount_for_uuid = get_mount_for_uuid;
    197   monitor_class->is_supported = is_supported;
    198 
    199   native_class->get_mount_for_mount_path = get_mount_for_mount_path;
    200 }
    201 
    202 static void
    203 mountpoints_changed (GUnixMountMonitor *mount_monitor,
    204 		     gpointer           user_data)
    205 {
    206   GUnixVolumeMonitor *unix_monitor = user_data;
    207 
    208   /* Update both to make sure volumes are created before mounts */
    209   update_volumes (unix_monitor);
    210   update_mounts (unix_monitor);
    211 }
    212 
    213 static void
    214 mounts_changed (GUnixMountMonitor *mount_monitor,
    215 		gpointer           user_data)
    216 {
    217   GUnixVolumeMonitor *unix_monitor = user_data;
    218 
    219   /* Update both to make sure volumes are created before mounts */
    220   update_volumes (unix_monitor);
    221   update_mounts (unix_monitor);
    222 }
    223 
    224 static void
    225 g_unix_volume_monitor_init (GUnixVolumeMonitor *unix_monitor)
    226 {
    227 
    228   unix_monitor->mount_monitor = g_unix_mount_monitor_new ();
    229 
    230   g_signal_connect (unix_monitor->mount_monitor,
    231 		    "mounts-changed", G_CALLBACK (mounts_changed),
    232 		    unix_monitor);
    233 
    234   g_signal_connect (unix_monitor->mount_monitor,
    235 		    "mountpoints-changed", G_CALLBACK (mountpoints_changed),
    236 		    unix_monitor);
    237 
    238   update_volumes (unix_monitor);
    239   update_mounts (unix_monitor);
    240 }
    241 
    242 /**
    243  * g_unix_volume_monitor_new:
    244  *
    245  * Returns:  a new #GVolumeMonitor.
    246  **/
    247 GVolumeMonitor *
    248 _g_unix_volume_monitor_new (void)
    249 {
    250   GUnixVolumeMonitor *monitor;
    251 
    252   monitor = g_object_new (G_TYPE_UNIX_VOLUME_MONITOR, NULL);
    253 
    254   return G_VOLUME_MONITOR (monitor);
    255 }
    256 
    257 static void
    258 diff_sorted_lists (GList         *list1,
    259                    GList         *list2,
    260                    GCompareFunc   compare,
    261 		   GList        **added,
    262                    GList        **removed)
    263 {
    264   int order;
    265 
    266   *added = *removed = NULL;
    267 
    268   while (list1 != NULL &&
    269 	 list2 != NULL)
    270     {
    271       order = (*compare) (list1->data, list2->data);
    272       if (order < 0)
    273 	{
    274 	  *removed = g_list_prepend (*removed, list1->data);
    275 	  list1 = list1->next;
    276 	}
    277       else if (order > 0)
    278 	{
    279 	  *added = g_list_prepend (*added, list2->data);
    280 	  list2 = list2->next;
    281 	}
    282       else
    283 	{ /* same item */
    284 	  list1 = list1->next;
    285 	  list2 = list2->next;
    286 	}
    287     }
    288 
    289   while (list1 != NULL)
    290     {
    291       *removed = g_list_prepend (*removed, list1->data);
    292       list1 = list1->next;
    293     }
    294   while (list2 != NULL)
    295     {
    296       *added = g_list_prepend (*added, list2->data);
    297       list2 = list2->next;
    298     }
    299 }
    300 
    301 /**
    302  * _g_unix_volume_monitor_lookup_volume_for_mount_path:
    303  * @monitor:
    304  * @mount_path:
    305  *
    306  * Returns:  #GUnixVolume for the given @mount_path.
    307  **/
    308 GUnixVolume *
    309 _g_unix_volume_monitor_lookup_volume_for_mount_path (GUnixVolumeMonitor *monitor,
    310                                                      const char         *mount_path)
    311 {
    312   GList *l;
    313 
    314   for (l = monitor->volumes; l != NULL; l = l->next)
    315     {
    316       GUnixVolume *volume = l->data;
    317 
    318       if (_g_unix_volume_has_mount_path (volume, mount_path))
    319 	return volume;
    320     }
    321 
    322   return NULL;
    323 }
    324 
    325 static GUnixMount *
    326 find_mount_by_mountpath (GUnixVolumeMonitor *monitor,
    327                          const char *mount_path)
    328 {
    329   GList *l;
    330 
    331   for (l = monitor->mounts; l != NULL; l = l->next)
    332     {
    333       GUnixMount *mount = l->data;
    334 
    335       if (_g_unix_mount_has_mount_path (mount, mount_path))
    336 	return mount;
    337     }
    338 
    339   return NULL;
    340 }
    341 
    342 static void
    343 update_volumes (GUnixVolumeMonitor *monitor)
    344 {
    345   GList *new_mountpoints;
    346   GList *removed, *added;
    347   GList *l;
    348   GUnixVolume *volume;
    349 
    350   new_mountpoints = g_unix_mount_points_get (NULL);
    351 
    352   new_mountpoints = g_list_sort (new_mountpoints, (GCompareFunc) g_unix_mount_point_compare);
    353 
    354   diff_sorted_lists (monitor->last_mountpoints,
    355 		     new_mountpoints, (GCompareFunc) g_unix_mount_point_compare,
    356 		     &added, &removed);
    357 
    358   for (l = removed; l != NULL; l = l->next)
    359     {
    360       GUnixMountPoint *mountpoint = l->data;
    361 
    362       volume = _g_unix_volume_monitor_lookup_volume_for_mount_path (monitor,
    363                                                                     g_unix_mount_point_get_mount_path (mountpoint));
    364       if (volume)
    365 	{
    366 	  _g_unix_volume_disconnected (volume);
    367 	  monitor->volumes = g_list_remove (monitor->volumes, volume);
    368 	  g_signal_emit_by_name (monitor, "volume-removed", volume);
    369 	  g_signal_emit_by_name (volume, "removed");
    370 	  g_object_unref (volume);
    371 	}
    372     }
    373 
    374   for (l = added; l != NULL; l = l->next)
    375     {
    376       GUnixMountPoint *mountpoint = l->data;
    377 
    378       volume = _g_unix_volume_new (G_VOLUME_MONITOR (monitor), mountpoint);
    379       if (volume)
    380 	{
    381 	  monitor->volumes = g_list_prepend (monitor->volumes, volume);
    382 	  g_signal_emit_by_name (monitor, "volume-added", volume);
    383 	}
    384     }
    385 
    386   g_list_free (added);
    387   g_list_free (removed);
    388   g_list_foreach (monitor->last_mountpoints,
    389 		  (GFunc)g_unix_mount_point_free, NULL);
    390   g_list_free (monitor->last_mountpoints);
    391   monitor->last_mountpoints = new_mountpoints;
    392 }
    393 
    394 static void
    395 update_mounts (GUnixVolumeMonitor *monitor)
    396 {
    397   GList *new_mounts;
    398   GList *removed, *added;
    399   GList *l;
    400   GUnixMount *mount;
    401   GUnixVolume *volume;
    402   const char *mount_path;
    403 
    404   new_mounts = g_unix_mounts_get (NULL);
    405 
    406   new_mounts = g_list_sort (new_mounts, (GCompareFunc) g_unix_mount_compare);
    407 
    408   diff_sorted_lists (monitor->last_mounts,
    409 		     new_mounts, (GCompareFunc) g_unix_mount_compare,
    410 		     &added, &removed);
    411 
    412   for (l = removed; l != NULL; l = l->next)
    413     {
    414       GUnixMountEntry *mount_entry = l->data;
    415 
    416       mount = find_mount_by_mountpath (monitor, g_unix_mount_get_mount_path (mount_entry));
    417       if (mount)
    418 	{
    419 	  _g_unix_mount_unmounted (mount);
    420 	  monitor->mounts = g_list_remove (monitor->mounts, mount);
    421 	  g_signal_emit_by_name (monitor, "mount-removed", mount);
    422 	  g_signal_emit_by_name (mount, "unmounted");
    423 	  g_object_unref (mount);
    424 	}
    425     }
    426 
    427   for (l = added; l != NULL; l = l->next)
    428     {
    429       GUnixMountEntry *mount_entry = l->data;
    430 
    431       mount_path = g_unix_mount_get_mount_path (mount_entry);
    432 
    433       volume = _g_unix_volume_monitor_lookup_volume_for_mount_path (monitor, mount_path);
    434       mount = _g_unix_mount_new (G_VOLUME_MONITOR (monitor), mount_entry, volume);
    435       if (mount)
    436 	{
    437 	  monitor->mounts = g_list_prepend (monitor->mounts, mount);
    438 	  g_signal_emit_by_name (monitor, "mount-added", mount);
    439 	}
    440     }
    441 
    442   g_list_free (added);
    443   g_list_free (removed);
    444   g_list_foreach (monitor->last_mounts,
    445 		  (GFunc)g_unix_mount_free, NULL);
    446   g_list_free (monitor->last_mounts);
    447   monitor->last_mounts = new_mounts;
    448 }
    449