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