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