(root)/
glib-2.79.0/
gio/
gunionvolumemonitor.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 "gunionvolumemonitor.h"
      32  #include "gmountprivate.h"
      33  #include "giomodule-priv.h"
      34  #ifdef G_OS_UNIX
      35  #include "gunixvolumemonitor.h"
      36  #endif
      37  #include "gnativevolumemonitor.h"
      38  
      39  #include "glibintl.h"
      40  
      41  
      42  struct _GUnionVolumeMonitor {
      43    GVolumeMonitor parent;
      44  
      45    GList *monitors;
      46  };
      47  
      48  static void g_union_volume_monitor_remove_monitor (GUnionVolumeMonitor *union_monitor,
      49  						   GVolumeMonitor *child_monitor);
      50  
      51  
      52  #define g_union_volume_monitor_get_type _g_union_volume_monitor_get_type
      53  G_DEFINE_TYPE (GUnionVolumeMonitor, g_union_volume_monitor, G_TYPE_VOLUME_MONITOR)
      54  
      55  static GRecMutex the_volume_monitor_mutex;
      56  
      57  static GUnionVolumeMonitor *the_volume_monitor = NULL;
      58  
      59  static void
      60  g_union_volume_monitor_finalize (GObject *object)
      61  {
      62    GUnionVolumeMonitor *monitor;
      63    GVolumeMonitor *child_monitor;
      64  
      65    monitor = G_UNION_VOLUME_MONITOR (object);
      66  
      67    while (monitor->monitors != NULL)
      68      {
      69        child_monitor = monitor->monitors->data;
      70        g_union_volume_monitor_remove_monitor (monitor, 
      71                                               child_monitor);
      72        g_object_unref (child_monitor);
      73      }
      74  
      75    G_OBJECT_CLASS (g_union_volume_monitor_parent_class)->finalize (object);
      76  }
      77  
      78  static void
      79  g_union_volume_monitor_dispose (GObject *object)
      80  {
      81    GUnionVolumeMonitor *monitor;
      82    GVolumeMonitor *child_monitor;
      83    GList *l;
      84  
      85    monitor = G_UNION_VOLUME_MONITOR (object);
      86  
      87    g_rec_mutex_lock (&the_volume_monitor_mutex);
      88    the_volume_monitor = NULL;
      89  
      90    for (l = monitor->monitors; l != NULL; l = l->next)
      91      {
      92        child_monitor = l->data;
      93        g_object_run_dispose (G_OBJECT (child_monitor));
      94      }
      95    
      96    g_rec_mutex_unlock (&the_volume_monitor_mutex);
      97  
      98    G_OBJECT_CLASS (g_union_volume_monitor_parent_class)->dispose (object);
      99  }
     100  
     101  static GList *
     102  get_mounts (GVolumeMonitor *volume_monitor)
     103  {
     104    GUnionVolumeMonitor *monitor;
     105    GVolumeMonitor *child_monitor;
     106    GList *res;
     107    GList *l;
     108    
     109    monitor = G_UNION_VOLUME_MONITOR (volume_monitor);
     110  
     111    res = NULL;
     112    
     113    g_rec_mutex_lock (&the_volume_monitor_mutex);
     114  
     115    for (l = monitor->monitors; l != NULL; l = l->next)
     116      {
     117        child_monitor = l->data;
     118  
     119        res = g_list_concat (res, g_volume_monitor_get_mounts (child_monitor));
     120      }
     121    
     122    g_rec_mutex_unlock (&the_volume_monitor_mutex);
     123  
     124    return res;
     125  }
     126  
     127  static GList *
     128  get_volumes (GVolumeMonitor *volume_monitor)
     129  {
     130    GUnionVolumeMonitor *monitor;
     131    GVolumeMonitor *child_monitor;
     132    GList *res;
     133    GList *l;
     134    
     135    monitor = G_UNION_VOLUME_MONITOR (volume_monitor);
     136  
     137    res = NULL;
     138    
     139    g_rec_mutex_lock (&the_volume_monitor_mutex);
     140  
     141    for (l = monitor->monitors; l != NULL; l = l->next)
     142      {
     143        child_monitor = l->data;
     144  
     145        res = g_list_concat (res, g_volume_monitor_get_volumes (child_monitor));
     146      }
     147    
     148    g_rec_mutex_unlock (&the_volume_monitor_mutex);
     149  
     150    return res;
     151  }
     152  
     153  static GList *
     154  get_connected_drives (GVolumeMonitor *volume_monitor)
     155  {
     156    GUnionVolumeMonitor *monitor;
     157    GVolumeMonitor *child_monitor;
     158    GList *res;
     159    GList *l;
     160    
     161    monitor = G_UNION_VOLUME_MONITOR (volume_monitor);
     162  
     163    res = NULL;
     164    
     165    g_rec_mutex_lock (&the_volume_monitor_mutex);
     166  
     167    for (l = monitor->monitors; l != NULL; l = l->next)
     168      {
     169        child_monitor = l->data;
     170  
     171        res = g_list_concat (res, g_volume_monitor_get_connected_drives (child_monitor));
     172      }
     173    
     174    g_rec_mutex_unlock (&the_volume_monitor_mutex);
     175  
     176    return res;
     177  }
     178  
     179  static GVolume *
     180  get_volume_for_uuid (GVolumeMonitor *volume_monitor, const char *uuid)
     181  {
     182    GUnionVolumeMonitor *monitor;
     183    GVolumeMonitor *child_monitor;
     184    GVolume *volume;
     185    GList *l;
     186    
     187    monitor = G_UNION_VOLUME_MONITOR (volume_monitor);
     188  
     189    volume = NULL;
     190    
     191    g_rec_mutex_lock (&the_volume_monitor_mutex);
     192  
     193    for (l = monitor->monitors; l != NULL; l = l->next)
     194      {
     195        child_monitor = l->data;
     196  
     197        volume = g_volume_monitor_get_volume_for_uuid (child_monitor, uuid);
     198        if (volume != NULL)
     199          break;
     200  
     201      }
     202    
     203    g_rec_mutex_unlock (&the_volume_monitor_mutex);
     204  
     205    return volume;
     206  }
     207  
     208  static GMount *
     209  get_mount_for_uuid (GVolumeMonitor *volume_monitor, const char *uuid)
     210  {
     211    GUnionVolumeMonitor *monitor;
     212    GVolumeMonitor *child_monitor;
     213    GMount *mount;
     214    GList *l;
     215    
     216    monitor = G_UNION_VOLUME_MONITOR (volume_monitor);
     217  
     218    mount = NULL;
     219    
     220    g_rec_mutex_lock (&the_volume_monitor_mutex);
     221  
     222    for (l = monitor->monitors; l != NULL; l = l->next)
     223      {
     224        child_monitor = l->data;
     225  
     226        mount = g_volume_monitor_get_mount_for_uuid (child_monitor, uuid);
     227        if (mount != NULL)
     228          break;
     229  
     230      }
     231    
     232    g_rec_mutex_unlock (&the_volume_monitor_mutex);
     233  
     234    return mount;
     235  }
     236  
     237  static void
     238  g_union_volume_monitor_class_init (GUnionVolumeMonitorClass *klass)
     239  {
     240    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
     241    GVolumeMonitorClass *monitor_class = G_VOLUME_MONITOR_CLASS (klass);
     242    
     243    gobject_class->finalize = g_union_volume_monitor_finalize;
     244    gobject_class->dispose = g_union_volume_monitor_dispose;
     245  
     246    monitor_class->get_connected_drives = get_connected_drives;
     247    monitor_class->get_volumes = get_volumes;
     248    monitor_class->get_mounts = get_mounts;
     249    monitor_class->get_volume_for_uuid = get_volume_for_uuid;
     250    monitor_class->get_mount_for_uuid = get_mount_for_uuid;
     251  }
     252  
     253  static void
     254  child_volume_added (GVolumeMonitor      *child_monitor,
     255                      GVolume    *child_volume,
     256                      GUnionVolumeMonitor *union_monitor)
     257  {
     258    g_signal_emit_by_name (union_monitor,
     259  			 "volume-added",
     260  			 child_volume);
     261  }
     262  
     263  static void
     264  child_volume_removed (GVolumeMonitor      *child_monitor,
     265                        GVolume    *child_volume,
     266                        GUnionVolumeMonitor *union_monitor)
     267  {
     268    g_signal_emit_by_name (union_monitor,
     269  			 "volume-removed",
     270  			 child_volume);
     271  }
     272  
     273  static void
     274  child_volume_changed (GVolumeMonitor      *child_monitor,
     275                        GVolume    *child_volume,
     276                        GUnionVolumeMonitor *union_monitor)
     277  {
     278    g_signal_emit_by_name (union_monitor,
     279  			 "volume-changed",
     280  			 child_volume);
     281  }
     282  
     283  static void
     284  child_mount_added (GVolumeMonitor      *child_monitor,
     285                     GMount              *child_mount,
     286                     GUnionVolumeMonitor *union_monitor)
     287  {
     288    g_signal_emit_by_name (union_monitor,
     289                           "mount-added",
     290                           child_mount);
     291  }
     292  
     293  static void
     294  child_mount_removed (GVolumeMonitor      *child_monitor,
     295                       GMount              *child_mount,
     296                       GUnionVolumeMonitor *union_monitor)
     297  {
     298    g_signal_emit_by_name (union_monitor,
     299  			 "mount-removed",
     300  			 child_mount);
     301  }
     302  
     303  static void
     304  child_mount_pre_unmount (GVolumeMonitor       *child_monitor,
     305                            GMount              *child_mount,
     306                            GUnionVolumeMonitor *union_monitor)
     307  {
     308    g_signal_emit_by_name (union_monitor,
     309  			 "mount-pre-unmount",
     310  			 child_mount);
     311  }
     312  
     313  
     314  static void
     315  child_mount_changed (GVolumeMonitor       *child_monitor,
     316                        GMount              *child_mount,
     317                        GUnionVolumeMonitor *union_monitor)
     318  {
     319    g_signal_emit_by_name (union_monitor,
     320  			 "mount-changed",
     321  			 child_mount);
     322  }
     323  
     324  static void
     325  child_drive_connected (GVolumeMonitor      *child_monitor,
     326                         GDrive              *child_drive,
     327                         GUnionVolumeMonitor *union_monitor)
     328  {
     329    g_signal_emit_by_name (union_monitor,
     330  			 "drive-connected",
     331  			 child_drive);
     332  }
     333  
     334  static void
     335  child_drive_disconnected (GVolumeMonitor      *child_monitor,
     336                            GDrive              *child_drive,
     337                            GUnionVolumeMonitor *union_monitor)
     338  {
     339    g_signal_emit_by_name (union_monitor,
     340  			 "drive-disconnected",
     341  			 child_drive);
     342  }
     343  
     344  static void
     345  child_drive_changed (GVolumeMonitor      *child_monitor,
     346                       GDrive             *child_drive,
     347                       GUnionVolumeMonitor *union_monitor)
     348  {
     349    g_signal_emit_by_name (union_monitor,
     350                           "drive-changed",
     351                           child_drive);
     352  }
     353  
     354  static void
     355  child_drive_eject_button (GVolumeMonitor      *child_monitor,
     356                            GDrive             *child_drive,
     357                            GUnionVolumeMonitor *union_monitor)
     358  {
     359    g_signal_emit_by_name (union_monitor,
     360                           "drive-eject-button",
     361                           child_drive);
     362  }
     363  
     364  static void
     365  child_drive_stop_button (GVolumeMonitor      *child_monitor,
     366                           GDrive             *child_drive,
     367                           GUnionVolumeMonitor *union_monitor)
     368  {
     369    g_signal_emit_by_name (union_monitor,
     370                           "drive-stop-button",
     371                           child_drive);
     372  }
     373  
     374  static void
     375  g_union_volume_monitor_add_monitor (GUnionVolumeMonitor *union_monitor,
     376                                      GVolumeMonitor      *volume_monitor)
     377  {
     378    if (g_list_find (union_monitor->monitors, volume_monitor))
     379      return;
     380  
     381    union_monitor->monitors =
     382      g_list_prepend (union_monitor->monitors,
     383  		    g_object_ref (volume_monitor));
     384  
     385    g_signal_connect (volume_monitor, "volume-added", (GCallback)child_volume_added, union_monitor);
     386    g_signal_connect (volume_monitor, "volume-removed", (GCallback)child_volume_removed, union_monitor);
     387    g_signal_connect (volume_monitor, "volume-changed", (GCallback)child_volume_changed, union_monitor);
     388    g_signal_connect (volume_monitor, "mount-added", (GCallback)child_mount_added, union_monitor);
     389    g_signal_connect (volume_monitor, "mount-removed", (GCallback)child_mount_removed, union_monitor);
     390    g_signal_connect (volume_monitor, "mount-pre-unmount", (GCallback)child_mount_pre_unmount, union_monitor);
     391    g_signal_connect (volume_monitor, "mount-changed", (GCallback)child_mount_changed, union_monitor);
     392    g_signal_connect (volume_monitor, "drive-connected", (GCallback)child_drive_connected, union_monitor);
     393    g_signal_connect (volume_monitor, "drive-disconnected", (GCallback)child_drive_disconnected, union_monitor);
     394    g_signal_connect (volume_monitor, "drive-changed", (GCallback)child_drive_changed, union_monitor);
     395    g_signal_connect (volume_monitor, "drive-eject-button", (GCallback)child_drive_eject_button, union_monitor);
     396    g_signal_connect (volume_monitor, "drive-stop-button", (GCallback)child_drive_stop_button, union_monitor);
     397  }
     398  
     399  static void
     400  g_union_volume_monitor_remove_monitor (GUnionVolumeMonitor *union_monitor,
     401                                         GVolumeMonitor      *child_monitor)
     402  {
     403    GList *l;
     404  
     405    l = g_list_find (union_monitor->monitors, child_monitor);
     406    if (l == NULL)
     407      return;
     408  
     409    union_monitor->monitors = g_list_delete_link (union_monitor->monitors, l);
     410  
     411    g_signal_handlers_disconnect_by_func (child_monitor, child_volume_added, union_monitor);
     412    g_signal_handlers_disconnect_by_func (child_monitor, child_volume_removed, union_monitor);
     413    g_signal_handlers_disconnect_by_func (child_monitor, child_volume_changed, union_monitor);
     414    g_signal_handlers_disconnect_by_func (child_monitor, child_mount_added, union_monitor);
     415    g_signal_handlers_disconnect_by_func (child_monitor, child_mount_removed, union_monitor);
     416    g_signal_handlers_disconnect_by_func (child_monitor, child_mount_pre_unmount, union_monitor);
     417    g_signal_handlers_disconnect_by_func (child_monitor, child_mount_changed, union_monitor);
     418    g_signal_handlers_disconnect_by_func (child_monitor, child_drive_connected, union_monitor);
     419    g_signal_handlers_disconnect_by_func (child_monitor, child_drive_disconnected, union_monitor);
     420    g_signal_handlers_disconnect_by_func (child_monitor, child_drive_changed, union_monitor);
     421    g_signal_handlers_disconnect_by_func (child_monitor, child_drive_eject_button, union_monitor);
     422    g_signal_handlers_disconnect_by_func (child_monitor, child_drive_stop_button, union_monitor);
     423  }
     424  
     425  static GType
     426  get_default_native_class (gpointer data)
     427  {
     428    return _g_io_module_get_default_type (G_NATIVE_VOLUME_MONITOR_EXTENSION_POINT_NAME,
     429                                          "GIO_USE_VOLUME_MONITOR",
     430                                          G_STRUCT_OFFSET (GVolumeMonitorClass, is_supported));
     431  }
     432  
     433  /* We return the class, with a ref taken.
     434   * This way we avoid unloading the class/module
     435   * between selecting the type and creating the
     436   * instance on the first call.
     437   */
     438  static GNativeVolumeMonitorClass *
     439  get_native_class (void)
     440  {
     441    static GOnce once_init = G_ONCE_INIT;
     442    GTypeClass *type_class;
     443  
     444    type_class = NULL;
     445    g_once (&once_init, (GThreadFunc)get_default_native_class, &type_class);
     446  
     447    if (type_class == NULL && once_init.retval != GUINT_TO_POINTER(G_TYPE_INVALID))
     448      type_class = g_type_class_ref ((GType)once_init.retval);
     449    
     450    return (GNativeVolumeMonitorClass *)type_class;
     451  }
     452  
     453  static void
     454  g_union_volume_monitor_init (GUnionVolumeMonitor *union_monitor)
     455  {
     456  }
     457  
     458  static void
     459  populate_union_monitor (GUnionVolumeMonitor *union_monitor)
     460  {
     461    GVolumeMonitor *monitor;
     462    GNativeVolumeMonitorClass *native_class;
     463    GVolumeMonitorClass *klass;
     464    GIOExtensionPoint *ep;
     465    GIOExtension *extension;
     466    GList *l;
     467  
     468    native_class = get_native_class ();
     469  
     470    if (native_class != NULL)
     471      {
     472        monitor = g_object_new (G_TYPE_FROM_CLASS (native_class), NULL);
     473        g_union_volume_monitor_add_monitor (union_monitor, monitor);
     474        g_object_unref (monitor);
     475        g_type_class_unref (native_class);
     476      }
     477  
     478    ep = g_io_extension_point_lookup (G_VOLUME_MONITOR_EXTENSION_POINT_NAME);
     479    for (l = g_io_extension_point_get_extensions (ep); l != NULL; l = l->next)
     480      {
     481        extension = l->data;
     482        
     483        klass = G_VOLUME_MONITOR_CLASS (g_io_extension_ref_class (extension));
     484        if (klass->is_supported == NULL || klass->is_supported())
     485  	{
     486  	  monitor = g_object_new (g_io_extension_get_type (extension), NULL);
     487  	  g_union_volume_monitor_add_monitor (union_monitor, monitor);
     488  	  g_object_unref (monitor);
     489  	}
     490        g_type_class_unref (klass);
     491      }
     492  }
     493  
     494  static GUnionVolumeMonitor *
     495  g_union_volume_monitor_new (void)
     496  {
     497    GUnionVolumeMonitor *monitor;
     498  
     499    monitor = g_object_new (G_TYPE_UNION_VOLUME_MONITOR, NULL);
     500    
     501    return monitor;
     502  }
     503  
     504  /**
     505   * g_volume_monitor_get:
     506   * 
     507   * Gets the volume monitor used by gio.
     508   *
     509   * Returns: (transfer full): a reference to the #GVolumeMonitor used by gio. Call
     510   *    g_object_unref() when done with it.
     511   **/
     512  GVolumeMonitor *
     513  g_volume_monitor_get (void)
     514  {
     515    GVolumeMonitor *vm;
     516    
     517    g_rec_mutex_lock (&the_volume_monitor_mutex);
     518  
     519    if (the_volume_monitor)
     520      vm = G_VOLUME_MONITOR (g_object_ref (the_volume_monitor));
     521    else
     522      {
     523        the_volume_monitor = g_union_volume_monitor_new ();
     524        populate_union_monitor (the_volume_monitor);
     525        vm = G_VOLUME_MONITOR (the_volume_monitor);
     526      }
     527    
     528    g_rec_mutex_unlock (&the_volume_monitor_mutex);
     529  
     530    return vm;
     531  }
     532  
     533  GMount *
     534  _g_mount_get_for_mount_path (const gchar  *mount_path,
     535  			     GCancellable *cancellable)
     536  {
     537    GNativeVolumeMonitorClass *klass;
     538    GMount *mount;
     539    
     540    klass = get_native_class ();
     541    if (klass == NULL)
     542      return NULL;
     543  
     544    mount = NULL;
     545  
     546    if (klass->get_mount_for_mount_path)
     547      mount = klass->get_mount_for_mount_path (mount_path, cancellable);
     548  
     549    /* TODO: How do we know this succeeded? Keep in mind that the native
     550     *       volume monitor may fail (e.g. not being able to connect to
     551     *       udisks). Is the get_mount_for_mount_path() method allowed to
     552     *       return NULL? Seems like it is ... probably the method needs
     553     *       to take a boolean and write if it succeeds or not.. Messy.
     554     *       Very messy.
     555     */
     556    
     557    g_type_class_unref (klass);
     558  
     559    return mount;
     560  }
     561  
     562  /**
     563   * g_volume_monitor_adopt_orphan_mount:
     564   * @mount: a #GMount object to find a parent for
     565   *
     566   * This function should be called by any #GVolumeMonitor
     567   * implementation when a new #GMount object is created that is not
     568   * associated with a #GVolume object. It must be called just before
     569   * emitting the @mount_added signal.
     570   *
     571   * If the return value is not %NULL, the caller must associate the
     572   * returned #GVolume object with the #GMount. This involves returning
     573   * it in its g_mount_get_volume() implementation. The caller must
     574   * also listen for the "removed" signal on the returned object
     575   * and give up its reference when handling that signal
     576   * 
     577   * Similarly, if implementing g_volume_monitor_adopt_orphan_mount(),
     578   * the implementor must take a reference to @mount and return it in
     579   * its g_volume_get_mount() implemented. Also, the implementor must
     580   * listen for the "unmounted" signal on @mount and give up its
     581   * reference upon handling that signal.
     582   *
     583   * There are two main use cases for this function.
     584   *
     585   * One is when implementing a user space file system driver that reads
     586   * blocks of a block device that is already represented by the native
     587   * volume monitor (for example a CD Audio file system driver). Such
     588   * a driver will generate its own #GMount object that needs to be
     589   * associated with the #GVolume object that represents the volume.
     590   *
     591   * The other is for implementing a #GVolumeMonitor whose sole purpose
     592   * is to return #GVolume objects representing entries in the users
     593   * "favorite servers" list or similar.
     594   *
     595   * Returns: (transfer full): the #GVolume object that is the parent for @mount or %NULL
     596   * if no wants to adopt the #GMount.
     597   *
     598   * Deprecated: 2.20: Instead of using this function, #GVolumeMonitor
     599   * implementations should instead create shadow mounts with the URI of
     600   * the mount they intend to adopt. See the proxy volume monitor in
     601   * gvfs for an example of this. Also see g_mount_is_shadowed(),
     602   * g_mount_shadow() and g_mount_unshadow() functions.
     603   */
     604  GVolume *
     605  g_volume_monitor_adopt_orphan_mount (GMount *mount)
     606  {
     607    GVolumeMonitor *child_monitor;
     608    GVolumeMonitorClass *child_monitor_class;
     609    GVolume *volume;
     610    GList *l;
     611  
     612    g_return_val_if_fail (mount != NULL, NULL);
     613  
     614    if (the_volume_monitor == NULL)
     615      return NULL;
     616  
     617    volume = NULL;
     618    
     619    g_rec_mutex_lock (&the_volume_monitor_mutex);
     620  
     621    for (l = the_volume_monitor->monitors; l != NULL; l = l->next)
     622      {
     623        child_monitor = l->data;
     624        child_monitor_class = G_VOLUME_MONITOR_GET_CLASS (child_monitor);
     625  
     626        if (child_monitor_class->adopt_orphan_mount != NULL)
     627          {
     628            volume = child_monitor_class->adopt_orphan_mount (mount, child_monitor);
     629            if (volume != NULL)
     630              break;
     631          }
     632      }
     633    
     634    g_rec_mutex_unlock (&the_volume_monitor_mutex);
     635  
     636    return volume;
     637  }