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 "gunixvolumemonitor.h"
     34 #include "gunixmount.h"
     35 #include "gunixmounts.h"
     36 #include "gunixvolume.h"
     37 #include "gmountprivate.h"
     38 #include "gmount.h"
     39 #include "gfile.h"
     40 #include "gvolumemonitor.h"
     41 #include "gthemedicon.h"
     42 #include "gsimpleasyncresult.h"
     43 #include "gioerror.h"
     44 #include "glibintl.h"
     45 /* for BUFSIZ */
     46 #include <stdio.h>
     47 
     48 #include "gioalias.h"
     49 
     50 struct _GUnixMount {
     51   GObject parent;
     52 
     53   GVolumeMonitor   *volume_monitor;
     54 
     55   GUnixVolume      *volume; /* owned by volume monitor */
     56 
     57   char *name;
     58   GIcon *icon;
     59   char *device_path;
     60   char *mount_path;
     61 
     62   gboolean can_eject;
     63 };
     64 
     65 static void g_unix_mount_mount_iface_init (GMountIface *iface);
     66 
     67 #define g_unix_mount_get_type _g_unix_mount_get_type
     68 G_DEFINE_TYPE_WITH_CODE (GUnixMount, g_unix_mount, G_TYPE_OBJECT,
     69 			 G_IMPLEMENT_INTERFACE (G_TYPE_MOUNT,
     70 						g_unix_mount_mount_iface_init))
     71 
     72 
     73 static void
     74 g_unix_mount_finalize (GObject *object)
     75 {
     76   GUnixMount *mount;
     77 
     78   mount = G_UNIX_MOUNT (object);
     79 
     80   if (mount->volume_monitor != NULL)
     81     g_object_unref (mount->volume_monitor);
     82 
     83   if (mount->volume)
     84     _g_unix_volume_unset_mount (mount->volume, mount);
     85 
     86   /* TODO: g_warn_if_fail (volume->volume == NULL); */
     87   g_object_unref (mount->icon);
     88   g_free (mount->name);
     89   g_free (mount->device_path);
     90   g_free (mount->mount_path);
     91 
     92   G_OBJECT_CLASS (g_unix_mount_parent_class)->finalize (object);
     93 }
     94 
     95 static void
     96 g_unix_mount_class_init (GUnixMountClass *klass)
     97 {
     98   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
     99 
    100   gobject_class->finalize = g_unix_mount_finalize;
    101 }
    102 
    103 static void
    104 g_unix_mount_init (GUnixMount *unix_mount)
    105 {
    106 }
    107 
    108 GUnixMount *
    109 _g_unix_mount_new (GVolumeMonitor  *volume_monitor,
    110                    GUnixMountEntry *mount_entry,
    111                    GUnixVolume     *volume)
    112 {
    113   GUnixMount *mount;
    114 
    115   /* No volume for mount: Ignore internal things */
    116   if (volume == NULL && !g_unix_mount_guess_should_display (mount_entry))
    117     return NULL;
    118 
    119   mount = g_object_new (G_TYPE_UNIX_MOUNT, NULL);
    120   mount->volume_monitor = volume_monitor != NULL ? g_object_ref (volume_monitor) : NULL;
    121   mount->device_path = g_strdup (g_unix_mount_get_device_path (mount_entry));
    122   mount->mount_path = g_strdup (g_unix_mount_get_mount_path (mount_entry));
    123   mount->can_eject = g_unix_mount_guess_can_eject (mount_entry);
    124 
    125   mount->name = g_unix_mount_guess_name (mount_entry);
    126   mount->icon = g_unix_mount_guess_icon (mount_entry);
    127 
    128   /* need to do this last */
    129   mount->volume = volume;
    130   if (volume != NULL)
    131     _g_unix_volume_set_mount (volume, mount);
    132 
    133   return mount;
    134 }
    135 
    136 void
    137 _g_unix_mount_unmounted (GUnixMount *mount)
    138 {
    139   if (mount->volume != NULL)
    140     {
    141       _g_unix_volume_unset_mount (mount->volume, mount);
    142       mount->volume = NULL;
    143       g_signal_emit_by_name (mount, "changed");
    144       /* there's really no need to emit mount_changed on the volume monitor
    145        * as we're going to be deleted.. */
    146     }
    147 }
    148 
    149 void
    150 _g_unix_mount_unset_volume (GUnixMount *mount,
    151                             GUnixVolume  *volume)
    152 {
    153   if (mount->volume == volume)
    154     {
    155       mount->volume = NULL;
    156       /* TODO: Emit changed in idle to avoid locking issues */
    157       g_signal_emit_by_name (mount, "changed");
    158       if (mount->volume_monitor != NULL)
    159         g_signal_emit_by_name (mount->volume_monitor, "mount-changed", mount);
    160     }
    161 }
    162 
    163 static GFile *
    164 g_unix_mount_get_root (GMount *mount)
    165 {
    166   GUnixMount *unix_mount = G_UNIX_MOUNT (mount);
    167 
    168   return g_file_new_for_path (unix_mount->mount_path);
    169 }
    170 
    171 static GIcon *
    172 g_unix_mount_get_icon (GMount *mount)
    173 {
    174   GUnixMount *unix_mount = G_UNIX_MOUNT (mount);
    175 
    176   return g_object_ref (unix_mount->icon);
    177 }
    178 
    179 static char *
    180 g_unix_mount_get_uuid (GMount *mount)
    181 {
    182   return NULL;
    183 }
    184 
    185 static char *
    186 g_unix_mount_get_name (GMount *mount)
    187 {
    188   GUnixMount *unix_mount = G_UNIX_MOUNT (mount);
    189 
    190   return g_strdup (unix_mount->name);
    191 }
    192 
    193 gboolean
    194 _g_unix_mount_has_mount_path (GUnixMount *mount,
    195                               const char  *mount_path)
    196 {
    197   return strcmp (mount->mount_path, mount_path) == 0;
    198 }
    199 
    200 static GDrive *
    201 g_unix_mount_get_drive (GMount *mount)
    202 {
    203   GUnixMount *unix_mount = G_UNIX_MOUNT (mount);
    204 
    205   if (unix_mount->volume != NULL)
    206     return g_volume_get_drive (G_VOLUME (unix_mount->volume));
    207 
    208   return NULL;
    209 }
    210 
    211 static GVolume *
    212 g_unix_mount_get_volume (GMount *mount)
    213 {
    214   GUnixMount *unix_mount = G_UNIX_MOUNT (mount);
    215 
    216   if (unix_mount->volume)
    217     return G_VOLUME (g_object_ref (unix_mount->volume));
    218 
    219   return NULL;
    220 }
    221 
    222 static gboolean
    223 g_unix_mount_can_unmount (GMount *mount)
    224 {
    225   return TRUE;
    226 }
    227 
    228 static gboolean
    229 g_unix_mount_can_eject (GMount *mount)
    230 {
    231   GUnixMount *unix_mount = G_UNIX_MOUNT (mount);
    232   return unix_mount->can_eject;
    233 }
    234 
    235 
    236 typedef struct {
    237   GUnixMount *unix_mount;
    238   GAsyncReadyCallback callback;
    239   gpointer user_data;
    240   GCancellable *cancellable;
    241   int error_fd;
    242   GIOChannel *error_channel;
    243   guint error_channel_source_id;
    244   GString *error_string;
    245   gchar **argv;
    246 } UnmountEjectOp;
    247 
    248 static void
    249 eject_unmount_cb (GPid pid, gint status, gpointer user_data)
    250 {
    251   UnmountEjectOp *data = user_data;
    252   GSimpleAsyncResult *simple;
    253 
    254   if (WEXITSTATUS (status) != 0)
    255     {
    256       GError *error;
    257       error = g_error_new_literal (G_IO_ERROR,
    258                                    G_IO_ERROR_FAILED,
    259                                    data->error_string->str);
    260       simple = g_simple_async_result_new_from_error (G_OBJECT (data->unix_mount),
    261                                                      data->callback,
    262                                                      data->user_data,
    263                                                      error);
    264       g_error_free (error);
    265     }
    266   else
    267     {
    268       simple = g_simple_async_result_new (G_OBJECT (data->unix_mount),
    269                                           data->callback,
    270                                           data->user_data,
    271                                           NULL);
    272     }
    273 
    274   g_simple_async_result_complete (simple);
    275   g_object_unref (simple);
    276 
    277   g_source_remove (data->error_channel_source_id);
    278   g_io_channel_unref (data->error_channel);
    279   g_string_free (data->error_string, TRUE);
    280   g_strfreev (data->argv);
    281   close (data->error_fd);
    282   g_spawn_close_pid (pid);
    283   g_free (data);
    284 }
    285 
    286 static gboolean
    287 eject_unmount_read_error (GIOChannel *channel,
    288                     GIOCondition condition,
    289                     gpointer user_data)
    290 {
    291   UnmountEjectOp *data = user_data;
    292   char buf[BUFSIZ];
    293   gsize bytes_read;
    294   GError *error;
    295   GIOStatus status;
    296 
    297   error = NULL;
    298 read:
    299   status = g_io_channel_read_chars (channel, buf, sizeof (buf), &bytes_read, &error);
    300   if (status == G_IO_STATUS_NORMAL)
    301    {
    302      g_string_append_len (data->error_string, buf, bytes_read);
    303      if (bytes_read == sizeof (buf))
    304         goto read;
    305    }
    306   else if (status == G_IO_STATUS_EOF)
    307     g_string_append_len (data->error_string, buf, bytes_read);
    308   else if (status == G_IO_STATUS_ERROR)
    309     {
    310       if (data->error_string->len > 0)
    311         g_string_append (data->error_string, "\n");
    312 
    313       g_string_append (data->error_string, error->message);
    314       g_error_free (error);
    315       return FALSE;
    316     }
    317 
    318   return TRUE;
    319 }
    320 
    321 static gboolean
    322 eject_unmount_do_cb (gpointer user_data)
    323 {
    324   UnmountEjectOp *data = (UnmountEjectOp *) user_data;
    325   GPid child_pid;
    326   GError *error = NULL;
    327 
    328   if (!g_spawn_async_with_pipes (NULL,         /* working dir */
    329                                  data->argv,
    330                                  NULL,         /* envp */
    331                                  G_SPAWN_DO_NOT_REAP_CHILD|G_SPAWN_SEARCH_PATH,
    332                                  NULL,         /* child_setup */
    333                                  NULL,         /* user_data for child_setup */
    334                                  &child_pid,
    335                                  NULL,           /* standard_input */
    336                                  NULL,           /* standard_output */
    337                                  &(data->error_fd),
    338                                  &error)) {
    339     g_assert (error != NULL);
    340     goto handle_error;
    341   }
    342 
    343   data->error_string = g_string_new ("");
    344 
    345   data->error_channel = g_io_channel_unix_new (data->error_fd);
    346   g_io_channel_set_flags (data->error_channel, G_IO_FLAG_NONBLOCK, &error);
    347   if (error != NULL)
    348     goto handle_error;
    349 
    350   data->error_channel_source_id = g_io_add_watch (data->error_channel, G_IO_IN, eject_unmount_read_error, data);
    351   g_child_watch_add (child_pid, eject_unmount_cb, data);
    352 
    353 handle_error:
    354   if (error != NULL) {
    355     GSimpleAsyncResult *simple;
    356     simple = g_simple_async_result_new_from_error (G_OBJECT (data->unix_mount),
    357                                                    data->callback,
    358                                                    data->user_data,
    359                                                    error);
    360     g_simple_async_result_complete (simple);
    361     g_object_unref (simple);
    362 
    363     if (data->error_string != NULL)
    364       g_string_free (data->error_string, TRUE);
    365 
    366     if (data->error_channel != NULL)
    367       g_io_channel_unref (data->error_channel);
    368 
    369     g_strfreev (data->argv);
    370     g_error_free (error);
    371     g_free (data);
    372   }
    373 
    374   return FALSE;
    375 }
    376 
    377 static void
    378 eject_unmount_do (GMount              *mount,
    379                   GCancellable        *cancellable,
    380                   GAsyncReadyCallback  callback,
    381                   gpointer             user_data,
    382                   char               **argv)
    383 {
    384   GUnixMount *unix_mount = G_UNIX_MOUNT (mount);
    385   UnmountEjectOp *data;
    386 
    387   data = g_new0 (UnmountEjectOp, 1);
    388   data->unix_mount = unix_mount;
    389   data->callback = callback;
    390   data->user_data = user_data;
    391   data->cancellable = cancellable;
    392   data->argv = g_strdupv (argv);
    393 
    394   if (unix_mount->volume_monitor != NULL)
    395     g_signal_emit_by_name (unix_mount->volume_monitor, "mount-pre-unmount", mount);
    396 
    397   g_timeout_add (500, (GSourceFunc) eject_unmount_do_cb, data);
    398 }
    399 
    400 static void
    401 g_unix_mount_unmount (GMount             *mount,
    402                       GMountUnmountFlags flags,
    403                       GCancellable        *cancellable,
    404                       GAsyncReadyCallback  callback,
    405                       gpointer             user_data)
    406 {
    407   GUnixMount *unix_mount = G_UNIX_MOUNT (mount);
    408   char *argv[] = {"umount", NULL, NULL};
    409 
    410   if (unix_mount->mount_path != NULL)
    411     argv[1] = unix_mount->mount_path;
    412   else
    413     argv[1] = unix_mount->device_path;
    414 
    415   eject_unmount_do (mount, cancellable, callback, user_data, argv);
    416 }
    417 
    418 static gboolean
    419 g_unix_mount_unmount_finish (GMount       *mount,
    420                              GAsyncResult  *result,
    421                              GError       **error)
    422 {
    423   return TRUE;
    424 }
    425 
    426 static void
    427 g_unix_mount_eject (GMount             *mount,
    428                     GMountUnmountFlags flags,
    429                     GCancellable        *cancellable,
    430                     GAsyncReadyCallback  callback,
    431                     gpointer             user_data)
    432 {
    433   GUnixMount *unix_mount = G_UNIX_MOUNT (mount);
    434   char *argv[] = {"eject", NULL, NULL};
    435 
    436   if (unix_mount->mount_path != NULL)
    437     argv[1] = unix_mount->mount_path;
    438   else
    439     argv[1] = unix_mount->device_path;
    440 
    441   eject_unmount_do (mount, cancellable, callback, user_data, argv);
    442 }
    443 
    444 static gboolean
    445 g_unix_mount_eject_finish (GMount       *mount,
    446                            GAsyncResult  *result,
    447                            GError       **error)
    448 {
    449   return TRUE;
    450 }
    451 
    452 static void
    453 g_unix_mount_mount_iface_init (GMountIface *iface)
    454 {
    455   iface->get_root = g_unix_mount_get_root;
    456   iface->get_name = g_unix_mount_get_name;
    457   iface->get_icon = g_unix_mount_get_icon;
    458   iface->get_uuid = g_unix_mount_get_uuid;
    459   iface->get_drive = g_unix_mount_get_drive;
    460   iface->get_volume = g_unix_mount_get_volume;
    461   iface->can_unmount = g_unix_mount_can_unmount;
    462   iface->can_eject = g_unix_mount_can_eject;
    463   iface->unmount = g_unix_mount_unmount;
    464   iface->unmount_finish = g_unix_mount_unmount_finish;
    465   iface->eject = g_unix_mount_eject;
    466   iface->eject_finish = g_unix_mount_eject_finish;
    467 }
    468