gunixvolume.c 11.9 KB
Newer Older
1 2
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */

3 4 5 6 7 8 9
/* GIO - GLib Input, Output and Streaming Library
 * 
 * Copyright (C) 2006-2007 Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11 12 13 14 15 16 17
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General
18
 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 20
 *
 * Author: Alexander Larsson <alexl@redhat.com>
21
 *         David Zeuthen <davidz@redhat.com>
22 23
 */

24
#include "config.h"
25 26

#include <string.h>
27 28
#include <sys/wait.h>
#include <unistd.h>
29 30 31

#include <glib.h>
#include "gunixvolume.h"
32
#include "gunixmount.h"
33
#include "gunixmounts.h"
34
#include "gthemedicon.h"
35
#include "gvolume.h"
36
#include "gvolumemonitor.h"
37
#include "gtask.h"
38
#include "gioerror.h"
39
#include "glibintl.h"
Christian Neumair's avatar
Christian Neumair committed
40 41
/* for BUFSIZ */
#include <stdio.h>
42

43

44 45 46
struct _GUnixVolume {
  GObject parent;

47 48 49 50 51
  GVolumeMonitor *volume_monitor;
  GUnixMount     *mount; /* owned by volume monitor */
  
  char *device_path;
  char *mount_path;
52 53
  gboolean can_eject;

54 55 56
  char *identifier;
  char *identifier_type;
  
57
  char *name;
58
  GIcon *icon;
59
  GIcon *symbolic_icon;
60 61 62 63
};

static void g_unix_volume_volume_iface_init (GVolumeIface *iface);

64
#define g_unix_volume_get_type _g_unix_volume_get_type
65 66 67 68 69 70 71 72 73 74 75
G_DEFINE_TYPE_WITH_CODE (GUnixVolume, g_unix_volume, G_TYPE_OBJECT,
			 G_IMPLEMENT_INTERFACE (G_TYPE_VOLUME,
						g_unix_volume_volume_iface_init))

static void
g_unix_volume_finalize (GObject *object)
{
  GUnixVolume *volume;
  
  volume = G_UNIX_VOLUME (object);

76 77 78 79 80
  if (volume->volume_monitor != NULL)
    g_object_unref (volume->volume_monitor);

  if (volume->mount)
    _g_unix_mount_unset_volume (volume->mount, volume);
81
  
82
  g_object_unref (volume->icon);
83
  g_object_unref (volume->symbolic_icon);
84 85 86
  g_free (volume->name);
  g_free (volume->mount_path);
  g_free (volume->device_path);
87 88
  g_free (volume->identifier);
  g_free (volume->identifier_type);
89

90
  G_OBJECT_CLASS (g_unix_volume_parent_class)->finalize (object);
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
}

static void
g_unix_volume_class_init (GUnixVolumeClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);

  gobject_class->finalize = g_unix_volume_finalize;
}

static void
g_unix_volume_init (GUnixVolume *unix_volume)
{
}

GUnixVolume *
107 108
_g_unix_volume_new (GVolumeMonitor  *volume_monitor,
                    GUnixMountPoint *mountpoint)
109 110 111
{
  GUnixVolume *volume;
  
112 113 114
  if (!(g_unix_mount_point_is_user_mountable (mountpoint) ||
	g_str_has_prefix (g_unix_mount_point_get_device_path (mountpoint), "/vol/")) ||
      g_unix_mount_point_is_loopback (mountpoint))
115 116 117
    return NULL;
  
  volume = g_object_new (G_TYPE_UNIX_VOLUME, NULL);
118 119 120
  volume->volume_monitor = volume_monitor != NULL ? g_object_ref (volume_monitor) : NULL;
  volume->mount_path = g_strdup (g_unix_mount_point_get_mount_path (mountpoint));
  volume->device_path = g_strdup (g_unix_mount_point_get_device_path (mountpoint));
121
  volume->can_eject = g_unix_mount_point_guess_can_eject (mountpoint);
122

123 124
  volume->name = g_unix_mount_point_guess_name (mountpoint);
  volume->icon = g_unix_mount_point_guess_icon (mountpoint);
125
  volume->symbolic_icon = g_unix_mount_point_guess_symbolic_icon (mountpoint);
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148


  if (strcmp (g_unix_mount_point_get_fs_type (mountpoint), "nfs") == 0)
    {
      volume->identifier_type = g_strdup (G_VOLUME_IDENTIFIER_KIND_NFS_MOUNT);
      volume->identifier = g_strdup (volume->device_path);
    }
  else if (g_str_has_prefix (volume->device_path, "LABEL="))
    {
      volume->identifier_type = g_strdup (G_VOLUME_IDENTIFIER_KIND_LABEL);
      volume->identifier = g_strdup (volume->device_path + 6);
    }
  else if (g_str_has_prefix (volume->device_path, "UUID="))
    {
      volume->identifier_type = g_strdup (G_VOLUME_IDENTIFIER_KIND_UUID);
      volume->identifier = g_strdup (volume->device_path + 5);
    }
  else if (g_path_is_absolute (volume->device_path))
    {
      volume->identifier_type = g_strdup (G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
      volume->identifier = g_strdup (volume->device_path);
    }
  
149 150 151 152
  return volume;
}

void
153
_g_unix_volume_disconnected (GUnixVolume *volume)
154
{
155
  if (volume->mount)
156
    {
157 158
      _g_unix_mount_unset_volume (volume->mount, volume);
      volume->mount = NULL;
159 160 161
    }
}

162
void
Matthias Clasen's avatar
Matthias Clasen committed
163 164
_g_unix_volume_set_mount (GUnixVolume *volume,
                          GUnixMount  *mount)
165 166 167 168 169 170 171 172 173 174 175 176
{
  if (volume->mount == mount)
    return;
  
  if (volume->mount)
    _g_unix_mount_unset_volume (volume->mount, volume);
  
  volume->mount = mount;
  
  /* TODO: Emit changed in idle to avoid locking issues */
  g_signal_emit_by_name (volume, "changed");
  if (volume->volume_monitor != NULL)
177
    g_signal_emit_by_name (volume->volume_monitor, "volume-changed", volume);
178 179
}

180
void
181 182
_g_unix_volume_unset_mount (GUnixVolume  *volume,
                            GUnixMount *mount)
183
{
184
  if (volume->mount == mount)
185
    {
186
      volume->mount = NULL;
187 188
      /* TODO: Emit changed in idle to avoid locking issues */
      g_signal_emit_by_name (volume, "changed");
189
      if (volume->volume_monitor != NULL)
190
        g_signal_emit_by_name (volume->volume_monitor, "volume-changed", volume);
191 192 193 194 195 196 197
    }
}

static GIcon *
g_unix_volume_get_icon (GVolume *volume)
{
  GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
198
  return g_object_ref (unix_volume->icon);
199 200
}

201 202 203 204 205 206 207
static GIcon *
g_unix_volume_get_symbolic_icon (GVolume *volume)
{
  GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
  return g_object_ref (unix_volume->symbolic_icon);
}

208 209 210 211 212 213 214
static char *
g_unix_volume_get_name (GVolume *volume)
{
  GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
  return g_strdup (unix_volume->name);
}

215 216 217 218 219 220
static char *
g_unix_volume_get_uuid (GVolume *volume)
{
  return NULL;
}

221 222
static gboolean
g_unix_volume_can_mount (GVolume *volume)
223
{
224
  return TRUE;
225 226
}

227 228 229 230 231 232 233
static gboolean
g_unix_volume_can_eject (GVolume *volume)
{
  GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
  return unix_volume->can_eject;
}

234 235 236 237
static gboolean
g_unix_volume_should_automount (GVolume *volume)
{
  /* We automount all local volumes because we don't even
Matthias Clasen's avatar
Matthias Clasen committed
238 239
   * make the internal stuff visible
   */
240 241 242
  return TRUE;
}

243 244 245 246 247 248
static GDrive *
g_unix_volume_get_drive (GVolume *volume)
{
  return NULL;
}

249 250
static GMount *
g_unix_volume_get_mount (GVolume *volume)
251
{
252 253 254
  GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);

  if (unix_volume->mount != NULL)
255
    return g_object_ref (G_MOUNT (unix_volume->mount));
256 257

  return NULL;
258 259
}

260 261 262

gboolean
_g_unix_volume_has_mount_path (GUnixVolume *volume,
Matthias Clasen's avatar
Matthias Clasen committed
263
                               const char  *mount_path)
264
{
265
  return strcmp (volume->mount_path, mount_path) == 0;
266 267
}

268
static void
269 270 271
eject_mount_done (GObject      *source,
                  GAsyncResult *result,
                  gpointer      user_data)
272
{
273
  GSubprocess *subprocess = G_SUBPROCESS (source);
274
  GTask *task = user_data;
275 276
  GError *error = NULL;
  gchar *stderr_str;
277
  GUnixVolume *unix_volume;
278

279
  if (!g_subprocess_communicate_utf8_finish (subprocess, result, NULL, &stderr_str, &error))
Christian Neumair's avatar
Christian Neumair committed
280
    {
281
      g_task_return_error (task, error);
Christian Neumair's avatar
Christian Neumair committed
282
      g_error_free (error);
283 284 285 286 287 288 289
    }
  else /* successful communication */
    {
      if (!g_subprocess_get_successful (subprocess))
        /* ...but bad exit code */
        g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, "%s", stderr_str);
      else
290 291 292 293 294 295
        {
          /* ...and successful exit code */
          unix_volume = G_UNIX_VOLUME (g_task_get_source_object (task));
          _g_unix_volume_monitor_update (G_UNIX_VOLUME_MONITOR (unix_volume->volume_monitor));
          g_task_return_boolean (task, TRUE);
        }
296 297

      g_free (stderr_str);
Christian Neumair's avatar
Christian Neumair committed
298
    }
299

300
  g_object_unref (task);
301 302 303
}

static void
304 305 306 307 308
eject_mount_do (GVolume              *volume,
                GCancellable         *cancellable,
                GAsyncReadyCallback   callback,
                gpointer              user_data,
                const gchar * const  *argv)
309
{
310 311
  GSubprocess *subprocess;
  GError *error = NULL;
312
  GTask *task;
Christian Neumair's avatar
Christian Neumair committed
313

314
  task = g_task_new (volume, cancellable, callback, user_data);
315
  g_task_set_source_tag (task, eject_mount_do);
Christian Neumair's avatar
Christian Neumair committed
316

317
  if (g_task_return_error_if_cancelled (task))
318 319 320 321
    {
      g_object_unref (task);
      return;
    }
322

323 324
  subprocess = g_subprocess_newv (argv, G_SUBPROCESS_FLAGS_STDOUT_SILENCE | G_SUBPROCESS_FLAGS_STDERR_PIPE, &error);
  g_assert_no_error (error);
Christian Neumair's avatar
Christian Neumair committed
325

326 327 328
  g_subprocess_communicate_utf8_async (subprocess, NULL,
                                       g_task_get_cancellable (task),
                                       eject_mount_done, task);
329 330
}

331
static void
Matthias Clasen's avatar
Matthias Clasen committed
332
g_unix_volume_mount (GVolume            *volume,
Alexander Larsson's avatar
Alexander Larsson committed
333
                     GMountMountFlags    flags,
334 335 336 337 338 339
                     GMountOperation     *mount_operation,
                     GCancellable        *cancellable,
                     GAsyncReadyCallback  callback,
                     gpointer             user_data)
{
  GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
340
  const gchar *argv[] = { "mount", NULL, NULL };
341 342 343 344 345 346 347 348 349

  if (unix_volume->mount_path != NULL)
    argv[1] = unix_volume->mount_path;
  else
    argv[1] = unix_volume->device_path;

  eject_mount_do (volume, cancellable, callback, user_data, argv);
}

350
static gboolean
351
g_unix_volume_mount_finish (GVolume        *volume,
Matthias Clasen's avatar
Matthias Clasen committed
352 353
                            GAsyncResult  *result,
                            GError       **error)
354
{
355 356 357
  g_return_val_if_fail (g_task_is_valid (result, volume), FALSE);

  return g_task_propagate_boolean (G_TASK (result), error);
358 359
}

360
static void
Matthias Clasen's avatar
Matthias Clasen committed
361
g_unix_volume_eject (GVolume             *volume,
362
                     GMountUnmountFlags   flags,
363 364 365 366 367
                     GCancellable        *cancellable,
                     GAsyncReadyCallback  callback,
                     gpointer             user_data)
{
  GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
368
  const gchar *argv[] = { "eject", NULL, NULL };
369 370 371 372 373 374 375

  argv[1] = unix_volume->device_path;

  eject_mount_do (volume, cancellable, callback, user_data, argv);
}

static gboolean
Matthias Clasen's avatar
Matthias Clasen committed
376
g_unix_volume_eject_finish (GVolume       *volume,
377 378 379
                            GAsyncResult  *result,
                            GError       **error)
{
380 381 382
  g_return_val_if_fail (g_task_is_valid (result, volume), FALSE);

  return g_task_propagate_boolean (G_TASK (result), error);
383 384
}

Matthias Clasen's avatar
Matthias Clasen committed
385 386 387
static gchar *
g_unix_volume_get_identifier (GVolume     *volume,
                              const gchar *kind)
388 389 390
{
  GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);

391 392
  if (unix_volume->identifier_type != NULL &&
      strcmp (kind, unix_volume->identifier_type) == 0)
393
    return g_strdup (unix_volume->identifier);
Matthias Clasen's avatar
Matthias Clasen committed
394

395 396 397
  return NULL;
}

Matthias Clasen's avatar
Matthias Clasen committed
398
static gchar **
399 400 401
g_unix_volume_enumerate_identifiers (GVolume *volume)
{
  GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
Matthias Clasen's avatar
Matthias Clasen committed
402
  gchar **res;
403 404 405

  if (unix_volume->identifier_type)
    {
Matthias Clasen's avatar
Matthias Clasen committed
406
      res = g_new (gchar *, 2);
407 408 409 410 411
      res[0] = g_strdup (unix_volume->identifier_type);
      res[1] = NULL;
    }
  else
    {
Matthias Clasen's avatar
Matthias Clasen committed
412
      res = g_new (gchar *, 1);
413 414 415 416 417 418
      res[0] = NULL;
    }

  return res;
}

419 420 421 422 423
static void
g_unix_volume_volume_iface_init (GVolumeIface *iface)
{
  iface->get_name = g_unix_volume_get_name;
  iface->get_icon = g_unix_volume_get_icon;
424
  iface->get_symbolic_icon = g_unix_volume_get_symbolic_icon;
425
  iface->get_uuid = g_unix_volume_get_uuid;
426
  iface->get_drive = g_unix_volume_get_drive;
427 428
  iface->get_mount = g_unix_volume_get_mount;
  iface->can_mount = g_unix_volume_can_mount;
429
  iface->can_eject = g_unix_volume_can_eject;
430
  iface->should_automount = g_unix_volume_should_automount;
431 432
  iface->mount_fn = g_unix_volume_mount;
  iface->mount_finish = g_unix_volume_mount_finish;
433 434
  iface->eject = g_unix_volume_eject;
  iface->eject_finish = g_unix_volume_eject_finish;
435 436
  iface->get_identifier = g_unix_volume_get_identifier;
  iface->enumerate_identifiers = g_unix_volume_enumerate_identifiers;
437
}