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