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 #include <sys/wait.h>
     30 #include <unistd.h>
     31 
     32 #include <glib.h>
     33 #include "gunixvolume.h"
     34 #include "gunixmount.h"
     35 #include "gunixmounts.h"
     36 #include "gthemedicon.h"
     37 #include "gvolume.h"
     38 #include "gvolumemonitor.h"
     39 #include "gsimpleasyncresult.h"
     40 #include "gioerror.h"
     41 #include "glibintl.h"
     42 /* for BUFSIZ */
     43 #include <stdio.h>
     44 
     45 #include "gioalias.h"
     46 
     47 struct _GUnixVolume {
     48   GObject parent;
     49 
     50   GVolumeMonitor *volume_monitor;
     51   GUnixMount     *mount; /* owned by volume monitor */
     52 
     53   char *device_path;
     54   char *mount_path;
     55   gboolean can_eject;
     56 
     57   char *identifier;
     58   char *identifier_type;
     59 
     60   char *name;
     61   GIcon *icon;
     62 };
     63 
     64 static void g_unix_volume_volume_iface_init (GVolumeIface *iface);
     65 
     66 #define g_unix_volume_get_type _g_unix_volume_get_type
     67 G_DEFINE_TYPE_WITH_CODE (GUnixVolume, g_unix_volume, G_TYPE_OBJECT,
     68 			 G_IMPLEMENT_INTERFACE (G_TYPE_VOLUME,
     69 						g_unix_volume_volume_iface_init))
     70 
     71 static void
     72 g_unix_volume_finalize (GObject *object)
     73 {
     74   GUnixVolume *volume;
     75 
     76   volume = G_UNIX_VOLUME (object);
     77 
     78   if (volume->volume_monitor != NULL)
     79     g_object_unref (volume->volume_monitor);
     80 
     81   if (volume->mount)
     82     _g_unix_mount_unset_volume (volume->mount, volume);
     83 
     84   g_object_unref (volume->icon);
     85   g_free (volume->name);
     86   g_free (volume->mount_path);
     87   g_free (volume->device_path);
     88   g_free (volume->identifier);
     89   g_free (volume->identifier_type);
     90 
     91   G_OBJECT_CLASS (g_unix_volume_parent_class)->finalize (object);
     92 }
     93 
     94 static void
     95 g_unix_volume_class_init (GUnixVolumeClass *klass)
     96 {
     97   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
     98 
     99   gobject_class->finalize = g_unix_volume_finalize;
    100 }
    101 
    102 static void
    103 g_unix_volume_init (GUnixVolume *unix_volume)
    104 {
    105 }
    106 
    107 /**
    108  * g_unix_volume_new:
    109  * @volume_monitor: a #GVolumeMonitor.
    110  * @mountpoint: a #GUnixMountPoint.
    111  *
    112  * Returns: a #GUnixVolume for the given #GUnixMountPoint.
    113  **/
    114 GUnixVolume *
    115 _g_unix_volume_new (GVolumeMonitor  *volume_monitor,
    116                     GUnixMountPoint *mountpoint)
    117 {
    118   GUnixVolume *volume;
    119 
    120   if (!(g_unix_mount_point_is_user_mountable (mountpoint) ||
    121 	g_str_has_prefix (g_unix_mount_point_get_device_path (mountpoint), "/vol/")) ||
    122       g_unix_mount_point_is_loopback (mountpoint))
    123     return NULL;
    124 
    125   volume = g_object_new (G_TYPE_UNIX_VOLUME, NULL);
    126   volume->volume_monitor = volume_monitor != NULL ? g_object_ref (volume_monitor) : NULL;
    127   volume->mount_path = g_strdup (g_unix_mount_point_get_mount_path (mountpoint));
    128   volume->device_path = g_strdup (g_unix_mount_point_get_device_path (mountpoint));
    129   volume->can_eject = g_unix_mount_point_guess_can_eject (mountpoint);
    130 
    131   volume->name = g_unix_mount_point_guess_name (mountpoint);
    132   volume->icon = g_unix_mount_point_guess_icon (mountpoint);
    133 
    134 
    135   if (strcmp (g_unix_mount_point_get_fs_type (mountpoint), "nfs") == 0)
    136     {
    137       volume->identifier_type = g_strdup (G_VOLUME_IDENTIFIER_KIND_NFS_MOUNT);
    138       volume->identifier = g_strdup (volume->device_path);
    139     }
    140   else if (g_str_has_prefix (volume->device_path, "LABEL="))
    141     {
    142       volume->identifier_type = g_strdup (G_VOLUME_IDENTIFIER_KIND_LABEL);
    143       volume->identifier = g_strdup (volume->device_path + 6);
    144     }
    145   else if (g_str_has_prefix (volume->device_path, "UUID="))
    146     {
    147       volume->identifier_type = g_strdup (G_VOLUME_IDENTIFIER_KIND_UUID);
    148       volume->identifier = g_strdup (volume->device_path + 5);
    149     }
    150   else if (g_path_is_absolute (volume->device_path))
    151     {
    152       volume->identifier_type = g_strdup (G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
    153       volume->identifier = g_strdup (volume->device_path);
    154     }
    155 
    156   return volume;
    157 }
    158 
    159 /**
    160  * g_unix_volume_disconnected:
    161  * @volume:
    162  *
    163  **/
    164 void
    165 _g_unix_volume_disconnected (GUnixVolume *volume)
    166 {
    167   if (volume->mount)
    168     {
    169       _g_unix_mount_unset_volume (volume->mount, volume);
    170       volume->mount = NULL;
    171     }
    172 }
    173 
    174 /**
    175  * g_unix_volume_set_mount:
    176  * @volume:
    177  * @mount:
    178  *
    179  **/
    180 void
    181 _g_unix_volume_set_mount (GUnixVolume  *volume,
    182                           GUnixMount *mount)
    183 {
    184   if (volume->mount == mount)
    185     return;
    186 
    187   if (volume->mount)
    188     _g_unix_mount_unset_volume (volume->mount, volume);
    189 
    190   volume->mount = mount;
    191 
    192   /* TODO: Emit changed in idle to avoid locking issues */
    193   g_signal_emit_by_name (volume, "changed");
    194   if (volume->volume_monitor != NULL)
    195     g_signal_emit_by_name (volume->volume_monitor, "volume-changed", volume);
    196 }
    197 
    198 /**
    199  * g_unix_volume_unset_mount:
    200  * @volume:
    201  * @mount:
    202  *
    203  **/
    204 void
    205 _g_unix_volume_unset_mount (GUnixVolume  *volume,
    206                             GUnixMount *mount)
    207 {
    208   if (volume->mount == mount)
    209     {
    210       volume->mount = NULL;
    211       /* TODO: Emit changed in idle to avoid locking issues */
    212       g_signal_emit_by_name (volume, "changed");
    213       if (volume->volume_monitor != NULL)
    214         g_signal_emit_by_name (volume->volume_monitor, "volume-changed", volume);
    215     }
    216 }
    217 
    218 static GIcon *
    219 g_unix_volume_get_icon (GVolume *volume)
    220 {
    221   GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
    222   return g_object_ref (unix_volume->icon);
    223 }
    224 
    225 static char *
    226 g_unix_volume_get_name (GVolume *volume)
    227 {
    228   GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
    229   return g_strdup (unix_volume->name);
    230 }
    231 
    232 static char *
    233 g_unix_volume_get_uuid (GVolume *volume)
    234 {
    235   return NULL;
    236 }
    237 
    238 static gboolean
    239 g_unix_volume_can_mount (GVolume *volume)
    240 {
    241   return TRUE;
    242 }
    243 
    244 static gboolean
    245 g_unix_volume_can_eject (GVolume *volume)
    246 {
    247   GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
    248   return unix_volume->can_eject;
    249 }
    250 
    251 static gboolean
    252 g_unix_volume_should_automount (GVolume *volume)
    253 {
    254   /* We automount all local volumes because we don't even
    255      make the internal stuff visible */
    256   return TRUE;
    257 }
    258 
    259 static GDrive *
    260 g_unix_volume_get_drive (GVolume *volume)
    261 {
    262   return NULL;
    263 }
    264 
    265 static GMount *
    266 g_unix_volume_get_mount (GVolume *volume)
    267 {
    268   GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
    269 
    270   if (unix_volume->mount != NULL)
    271     return g_object_ref (unix_volume->mount);
    272 
    273   return NULL;
    274 }
    275 
    276 
    277 gboolean
    278 _g_unix_volume_has_mount_path (GUnixVolume *volume,
    279                                          const char  *mount_path)
    280 {
    281   return strcmp (volume->mount_path, mount_path) == 0;
    282 }
    283 
    284 
    285 typedef struct {
    286   GUnixVolume *unix_volume;
    287   GAsyncReadyCallback callback;
    288   gpointer user_data;
    289   GCancellable *cancellable;
    290   int error_fd;
    291   GIOChannel *error_channel;
    292   guint error_channel_source_id;
    293   GString *error_string;
    294 } EjectMountOp;
    295 
    296 static void
    297 eject_mount_cb (GPid pid, gint status, gpointer user_data)
    298 {
    299   EjectMountOp *data = user_data;
    300   GSimpleAsyncResult *simple;
    301 
    302   if (WEXITSTATUS (status) != 0)
    303     {
    304       GError *error;
    305       error = g_error_new_literal (G_IO_ERROR,
    306                                    G_IO_ERROR_FAILED,
    307                                    data->error_string->str);
    308       simple = g_simple_async_result_new_from_error (G_OBJECT (data->unix_volume),
    309                                                      data->callback,
    310                                                      data->user_data,
    311                                                      error);
    312       g_error_free (error);
    313     }
    314   else
    315     {
    316       simple = g_simple_async_result_new (G_OBJECT (data->unix_volume),
    317                                           data->callback,
    318                                           data->user_data,
    319                                           NULL);
    320     }
    321 
    322   g_simple_async_result_complete (simple);
    323   g_object_unref (simple);
    324 
    325   g_source_remove (data->error_channel_source_id);
    326   g_io_channel_unref (data->error_channel);
    327   g_string_free (data->error_string, TRUE);
    328   close (data->error_fd);
    329   g_spawn_close_pid (pid);
    330   g_free (data);
    331 }
    332 
    333 static gboolean
    334 eject_mount_read_error (GIOChannel *channel,
    335                   GIOCondition condition,
    336                   gpointer user_data)
    337 {
    338   EjectMountOp *data = user_data;
    339   char buf[BUFSIZ];
    340   gsize bytes_read;
    341   GError *error;
    342   GIOStatus status;
    343 
    344   error = NULL;
    345 read:
    346   status = g_io_channel_read_chars (channel, buf, sizeof (buf), &bytes_read, &error);
    347   if (status == G_IO_STATUS_NORMAL)
    348    {
    349      g_string_append_len (data->error_string, buf, bytes_read);
    350      if (bytes_read == sizeof (buf))
    351         goto read;
    352    }
    353   else if (status == G_IO_STATUS_EOF)
    354     g_string_append_len (data->error_string, buf, bytes_read);
    355   else if (status == G_IO_STATUS_ERROR)
    356     {
    357       if (data->error_string->len > 0)
    358         g_string_append (data->error_string, "\n");
    359 
    360       g_string_append (data->error_string, error->message);
    361       g_error_free (error);
    362       return FALSE;
    363     }
    364 
    365   return TRUE;
    366 }
    367 
    368 static void
    369 eject_mount_do (GVolume             *volume,
    370                 GCancellable        *cancellable,
    371                 GAsyncReadyCallback  callback,
    372                 gpointer             user_data,
    373                 char               **argv)
    374 {
    375   GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
    376   EjectMountOp *data;
    377   GPid child_pid;
    378   GError *error;
    379 
    380   data = g_new0 (EjectMountOp, 1);
    381   data->unix_volume = unix_volume;
    382   data->callback = callback;
    383   data->user_data = user_data;
    384   data->cancellable = cancellable;
    385 
    386   error = NULL;
    387   if (!g_spawn_async_with_pipes (NULL,         /* working dir */
    388                                  argv,
    389                                  NULL,         /* envp */
    390                                  G_SPAWN_DO_NOT_REAP_CHILD|G_SPAWN_SEARCH_PATH,
    391                                  NULL,         /* child_setup */
    392                                  NULL,         /* user_data for child_setup */
    393                                  &child_pid,
    394                                  NULL,           /* standard_input */
    395                                  NULL,           /* standard_output */
    396                                  &(data->error_fd),
    397                                  &error)) {
    398     g_assert (error != NULL);
    399     goto handle_error;
    400   }
    401 
    402   data->error_string = g_string_new ("");
    403 
    404   data->error_channel = g_io_channel_unix_new (data->error_fd);
    405   g_io_channel_set_flags (data->error_channel, G_IO_FLAG_NONBLOCK, &error);
    406   if (error != NULL)
    407     goto handle_error;
    408 
    409   data->error_channel_source_id = g_io_add_watch (data->error_channel, G_IO_IN, eject_mount_read_error, data);
    410   g_child_watch_add (child_pid, eject_mount_cb, data);
    411 
    412 handle_error:
    413   if (error != NULL) {
    414     GSimpleAsyncResult *simple;
    415     simple = g_simple_async_result_new_from_error (G_OBJECT (data->unix_volume),
    416                                                    data->callback,
    417                                                    data->user_data,
    418                                                    error);
    419     g_simple_async_result_complete (simple);
    420     g_object_unref (simple);
    421 
    422     if (data->error_string != NULL)
    423       g_string_free (data->error_string, TRUE);
    424 
    425     if (data->error_channel != NULL)
    426       g_io_channel_unref (data->error_channel);
    427 
    428     g_error_free (error);
    429     g_free (data);
    430   }
    431 }
    432 
    433 
    434 static void
    435 g_unix_volume_mount (GVolume    *volume,
    436                      GMountMountFlags    flags,
    437                      GMountOperation     *mount_operation,
    438                      GCancellable        *cancellable,
    439                      GAsyncReadyCallback  callback,
    440                      gpointer             user_data)
    441 {
    442   GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
    443   char *argv[] = {"mount", NULL, NULL};
    444 
    445   if (unix_volume->mount_path != NULL)
    446     argv[1] = unix_volume->mount_path;
    447   else
    448     argv[1] = unix_volume->device_path;
    449 
    450   eject_mount_do (volume, cancellable, callback, user_data, argv);
    451 }
    452 
    453 static gboolean
    454 g_unix_volume_mount_finish (GVolume        *volume,
    455                            GAsyncResult  *result,
    456                            GError       **error)
    457 {
    458   return TRUE;
    459 }
    460 
    461 static void
    462 g_unix_volume_eject (GVolume    *volume,
    463                      GMountUnmountFlags   flags,
    464                      GCancellable        *cancellable,
    465                      GAsyncReadyCallback  callback,
    466                      gpointer             user_data)
    467 {
    468   GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
    469   char *argv[] = {"eject", NULL, NULL};
    470 
    471   argv[1] = unix_volume->device_path;
    472 
    473   eject_mount_do (volume, cancellable, callback, user_data, argv);
    474 }
    475 
    476 static gboolean
    477 g_unix_volume_eject_finish (GVolume        *volume,
    478                             GAsyncResult  *result,
    479                             GError       **error)
    480 {
    481   return TRUE;
    482 }
    483 
    484 static char *
    485 g_unix_volume_get_identifier (GVolume              *volume,
    486                               const char          *kind)
    487 {
    488   GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
    489 
    490   if (unix_volume->identifier_type != NULL &&
    491       strcmp (kind, unix_volume->identifier_type) == 0)
    492     return g_strdup (unix_volume->identifier);
    493   return NULL;
    494 }
    495 
    496 static char **
    497 g_unix_volume_enumerate_identifiers (GVolume *volume)
    498 {
    499   GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
    500   char **res;
    501 
    502   if (unix_volume->identifier_type)
    503     {
    504       res = g_new (char *, 2);
    505       res[0] = g_strdup (unix_volume->identifier_type);
    506       res[1] = NULL;
    507     }
    508   else
    509     {
    510       res = g_new (char *, 1);
    511       res[0] = NULL;
    512     }
    513 
    514   return res;
    515 }
    516 
    517 static void
    518 g_unix_volume_volume_iface_init (GVolumeIface *iface)
    519 {
    520   iface->get_name = g_unix_volume_get_name;
    521   iface->get_icon = g_unix_volume_get_icon;
    522   iface->get_uuid = g_unix_volume_get_uuid;
    523   iface->get_drive = g_unix_volume_get_drive;
    524   iface->get_mount = g_unix_volume_get_mount;
    525   iface->can_mount = g_unix_volume_can_mount;
    526   iface->can_eject = g_unix_volume_can_eject;
    527   iface->should_automount = g_unix_volume_should_automount;
    528   iface->mount_fn = g_unix_volume_mount;
    529   iface->mount_finish = g_unix_volume_mount_finish;
    530   iface->eject = g_unix_volume_eject;
    531   iface->eject_finish = g_unix_volume_eject_finish;
    532   iface->get_identifier = g_unix_volume_get_identifier;
    533   iface->enumerate_identifiers = g_unix_volume_enumerate_identifiers;
    534 }
    535