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