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