Implement this function by moving bits from glocalfileinfo.c
[platform/upstream/glib.git] / gio / gunionvolumemonitor.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
30 #include <glib.h>
31 #include "gunionvolumemonitor.h"
32 #include "gmountprivate.h"
33 #include "giomodule-priv.h"
34 #ifdef G_OS_UNIX
35 #include "gunixvolumemonitor.h"
36 #endif
37 #include "gnativevolumemonitor.h"
38
39 #include "glibintl.h"
40
41 #include "gioalias.h"
42
43 struct _GUnionVolumeMonitor {
44   GVolumeMonitor parent;
45
46   GList *monitors;
47 };
48
49 static void g_union_volume_monitor_remove_monitor (GUnionVolumeMonitor *union_monitor,
50                                                    GVolumeMonitor *child_monitor);
51
52
53 #define g_union_volume_monitor_get_type _g_union_volume_monitor_get_type
54 G_DEFINE_TYPE (GUnionVolumeMonitor, g_union_volume_monitor, G_TYPE_VOLUME_MONITOR);
55
56 static GStaticRecMutex the_volume_monitor_mutex = G_STATIC_REC_MUTEX_INIT;
57
58 static GUnionVolumeMonitor *the_volume_monitor = NULL;
59
60 static void
61 g_union_volume_monitor_finalize (GObject *object)
62 {
63   GUnionVolumeMonitor *monitor;
64   GVolumeMonitor *child_monitor;
65
66   monitor = G_UNION_VOLUME_MONITOR (object);
67
68   while (monitor->monitors != NULL) {
69     child_monitor = monitor->monitors->data;
70     g_union_volume_monitor_remove_monitor (monitor, 
71                                            child_monitor);
72     g_object_unref (child_monitor);
73   }
74
75   
76   if (G_OBJECT_CLASS (g_union_volume_monitor_parent_class)->finalize)
77     (*G_OBJECT_CLASS (g_union_volume_monitor_parent_class)->finalize) (object);
78 }
79
80 static void
81 g_union_volume_monitor_dispose (GObject *object)
82 {
83   GUnionVolumeMonitor *monitor;
84   
85   monitor = G_UNION_VOLUME_MONITOR (object);
86
87   g_static_rec_mutex_lock (&the_volume_monitor_mutex);
88   the_volume_monitor = NULL;
89   g_static_rec_mutex_unlock (&the_volume_monitor_mutex);
90   
91   if (G_OBJECT_CLASS (g_union_volume_monitor_parent_class)->dispose)
92     (*G_OBJECT_CLASS (g_union_volume_monitor_parent_class)->dispose) (object);
93 }
94
95 static GList *
96 get_mounts (GVolumeMonitor *volume_monitor)
97 {
98   GUnionVolumeMonitor *monitor;
99   GVolumeMonitor *child_monitor;
100   GList *res;
101   GList *l;
102   
103   monitor = G_UNION_VOLUME_MONITOR (volume_monitor);
104
105   res = NULL;
106   
107   g_static_rec_mutex_lock (&the_volume_monitor_mutex);
108
109   for (l = monitor->monitors; l != NULL; l = l->next)
110     {
111       child_monitor = l->data;
112
113       res = g_list_concat (res, g_volume_monitor_get_mounts (child_monitor));
114     }
115   
116   g_static_rec_mutex_unlock (&the_volume_monitor_mutex);
117
118   return res;
119 }
120
121 static GList *
122 get_volumes (GVolumeMonitor *volume_monitor)
123 {
124   GUnionVolumeMonitor *monitor;
125   GVolumeMonitor *child_monitor;
126   GList *res;
127   GList *l;
128   
129   monitor = G_UNION_VOLUME_MONITOR (volume_monitor);
130
131   res = NULL;
132   
133   g_static_rec_mutex_lock (&the_volume_monitor_mutex);
134
135   for (l = monitor->monitors; l != NULL; l = l->next)
136     {
137       child_monitor = l->data;
138
139       res = g_list_concat (res, g_volume_monitor_get_volumes (child_monitor));
140     }
141   
142   g_static_rec_mutex_unlock (&the_volume_monitor_mutex);
143
144   return res;
145 }
146
147 static GList *
148 get_connected_drives (GVolumeMonitor *volume_monitor)
149 {
150   GUnionVolumeMonitor *monitor;
151   GVolumeMonitor *child_monitor;
152   GList *res;
153   GList *l;
154   
155   monitor = G_UNION_VOLUME_MONITOR (volume_monitor);
156
157   res = NULL;
158   
159   g_static_rec_mutex_lock (&the_volume_monitor_mutex);
160
161   for (l = monitor->monitors; l != NULL; l = l->next)
162     {
163       child_monitor = l->data;
164
165       res = g_list_concat (res, g_volume_monitor_get_connected_drives (child_monitor));
166     }
167   
168   g_static_rec_mutex_unlock (&the_volume_monitor_mutex);
169
170   return res;
171 }
172
173 static GVolume *
174 get_volume_for_uuid (GVolumeMonitor *volume_monitor, const char *uuid)
175 {
176   GUnionVolumeMonitor *monitor;
177   GVolumeMonitor *child_monitor;
178   GVolume *volume;
179   GList *l;
180   
181   monitor = G_UNION_VOLUME_MONITOR (volume_monitor);
182
183   volume = NULL;
184   
185   g_static_rec_mutex_lock (&the_volume_monitor_mutex);
186
187   for (l = monitor->monitors; l != NULL; l = l->next)
188     {
189       child_monitor = l->data;
190
191       volume = g_volume_monitor_get_volume_for_uuid (child_monitor, uuid);
192       if (volume != NULL)
193         break;
194
195     }
196   
197   g_static_rec_mutex_unlock (&the_volume_monitor_mutex);
198
199   return volume;
200 }
201
202 static GMount *
203 get_mount_for_uuid (GVolumeMonitor *volume_monitor, const char *uuid)
204 {
205   GUnionVolumeMonitor *monitor;
206   GVolumeMonitor *child_monitor;
207   GMount *mount;
208   GList *l;
209   
210   monitor = G_UNION_VOLUME_MONITOR (volume_monitor);
211
212   mount = NULL;
213   
214   g_static_rec_mutex_lock (&the_volume_monitor_mutex);
215
216   for (l = monitor->monitors; l != NULL; l = l->next)
217     {
218       child_monitor = l->data;
219
220       mount = g_volume_monitor_get_mount_for_uuid (child_monitor, uuid);
221       if (mount != NULL)
222         break;
223
224     }
225   
226   g_static_rec_mutex_unlock (&the_volume_monitor_mutex);
227
228   return mount;
229 }
230
231 static void
232 g_union_volume_monitor_class_init (GUnionVolumeMonitorClass *klass)
233 {
234   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
235   GVolumeMonitorClass *monitor_class = G_VOLUME_MONITOR_CLASS (klass);
236   
237   gobject_class->finalize = g_union_volume_monitor_finalize;
238   gobject_class->dispose = g_union_volume_monitor_dispose;
239
240   monitor_class->get_connected_drives = get_connected_drives;
241   monitor_class->get_volumes = get_volumes;
242   monitor_class->get_mounts = get_mounts;
243   monitor_class->get_volume_for_uuid = get_volume_for_uuid;
244   monitor_class->get_mount_for_uuid = get_mount_for_uuid;
245 }
246
247 static void
248 child_volume_added (GVolumeMonitor      *child_monitor,
249                     GVolume    *child_volume,
250                     GUnionVolumeMonitor *union_monitor)
251 {
252   g_signal_emit_by_name (union_monitor,
253                          "volume_added",
254                          child_volume);
255 }
256
257 static void
258 child_volume_removed (GVolumeMonitor      *child_monitor,
259                       GVolume    *child_volume,
260                       GUnionVolumeMonitor *union_monitor)
261 {
262   g_signal_emit_by_name (union_monitor,
263                          "volume_removed",
264                          child_volume);
265 }
266
267 static void
268 child_volume_changed (GVolumeMonitor      *child_monitor,
269                       GVolume    *child_volume,
270                       GUnionVolumeMonitor *union_monitor)
271 {
272   g_signal_emit_by_name (union_monitor,
273                          "volume_changed",
274                          child_volume);
275 }
276
277 static void
278 child_mount_added (GVolumeMonitor      *child_monitor,
279                    GMount              *child_mount,
280                    GUnionVolumeMonitor *union_monitor)
281 {
282   g_signal_emit_by_name (union_monitor,
283                          "mount_added",
284                          child_mount);
285 }
286
287 static void
288 child_mount_removed (GVolumeMonitor      *child_monitor,
289                      GMount              *child_mount,
290                      GUnionVolumeMonitor *union_monitor)
291 {
292   g_signal_emit_by_name (union_monitor,
293                          "mount_removed",
294                          child_mount);
295 }
296
297 static void
298 child_mount_pre_unmount (GVolumeMonitor       *child_monitor,
299                           GMount              *child_mount,
300                           GUnionVolumeMonitor *union_monitor)
301 {
302   g_signal_emit_by_name (union_monitor,
303                          "mount_pre_unmount",
304                          child_mount);
305 }
306
307
308 static void
309 child_mount_changed (GVolumeMonitor       *child_monitor,
310                       GMount              *child_mount,
311                       GUnionVolumeMonitor *union_monitor)
312 {
313   g_signal_emit_by_name (union_monitor,
314                          "mount_changed",
315                          child_mount);
316 }
317
318 static void
319 child_drive_connected (GVolumeMonitor      *child_monitor,
320                        GDrive              *child_drive,
321                        GUnionVolumeMonitor *union_monitor)
322 {
323   g_signal_emit_by_name (union_monitor,
324                          "drive_connected",
325                          child_drive);
326 }
327
328 static void
329 child_drive_disconnected (GVolumeMonitor      *child_monitor,
330                           GDrive              *child_drive,
331                           GUnionVolumeMonitor *union_monitor)
332 {
333   g_signal_emit_by_name (union_monitor,
334                          "drive_disconnected",
335                          child_drive);
336 }
337
338 static void
339 child_drive_changed (GVolumeMonitor      *child_monitor,
340                      GDrive             *child_drive,
341                      GUnionVolumeMonitor *union_monitor)
342 {
343   g_signal_emit_by_name (union_monitor,
344                          "drive_changed",
345                          child_drive);
346 }
347
348 static void
349 g_union_volume_monitor_add_monitor (GUnionVolumeMonitor *union_monitor,
350                                     GVolumeMonitor      *volume_monitor)
351 {
352   if (g_list_find (union_monitor->monitors, volume_monitor))
353     return;
354
355   union_monitor->monitors =
356     g_list_prepend (union_monitor->monitors,
357                     g_object_ref (volume_monitor));
358
359   g_signal_connect (volume_monitor, "volume_added", (GCallback)child_volume_added, union_monitor);
360   g_signal_connect (volume_monitor, "volume_removed", (GCallback)child_volume_removed, union_monitor);
361   g_signal_connect (volume_monitor, "volume_changed", (GCallback)child_volume_changed, union_monitor);
362   g_signal_connect (volume_monitor, "mount_added", (GCallback)child_mount_added, union_monitor);
363   g_signal_connect (volume_monitor, "mount_removed", (GCallback)child_mount_removed, union_monitor);
364   g_signal_connect (volume_monitor, "mount_pre_unmount", (GCallback)child_mount_pre_unmount, union_monitor);
365   g_signal_connect (volume_monitor, "mount_changed", (GCallback)child_mount_changed, union_monitor);
366   g_signal_connect (volume_monitor, "drive_connected", (GCallback)child_drive_connected, union_monitor);
367   g_signal_connect (volume_monitor, "drive_disconnected", (GCallback)child_drive_disconnected, union_monitor);
368   g_signal_connect (volume_monitor, "drive_changed", (GCallback)child_drive_changed, union_monitor);
369 }
370
371 static void
372 g_union_volume_monitor_remove_monitor (GUnionVolumeMonitor *union_monitor,
373                                        GVolumeMonitor      *child_monitor)
374 {
375   GList *l;
376
377   l = g_list_find (union_monitor->monitors, child_monitor);
378   if (l == NULL)
379     return;
380
381   union_monitor->monitors = g_list_delete_link (union_monitor->monitors, l);
382
383   g_signal_handlers_disconnect_by_func (child_monitor, child_volume_added, union_monitor);
384   g_signal_handlers_disconnect_by_func (child_monitor, child_volume_removed, union_monitor);
385   g_signal_handlers_disconnect_by_func (child_monitor, child_volume_changed, union_monitor);
386   g_signal_handlers_disconnect_by_func (child_monitor, child_mount_added, union_monitor);
387   g_signal_handlers_disconnect_by_func (child_monitor, child_mount_removed, union_monitor);
388   g_signal_handlers_disconnect_by_func (child_monitor, child_mount_pre_unmount, union_monitor);
389   g_signal_handlers_disconnect_by_func (child_monitor, child_mount_changed, union_monitor);
390   g_signal_handlers_disconnect_by_func (child_monitor, child_drive_connected, union_monitor);
391   g_signal_handlers_disconnect_by_func (child_monitor, child_drive_disconnected, union_monitor);
392   g_signal_handlers_disconnect_by_func (child_monitor, child_drive_changed, union_monitor);
393 }
394
395 static GType
396 get_default_native_class (gpointer data)
397 {
398   GNativeVolumeMonitorClass *klass, *native_class, **native_class_out;
399   const char *use_this;
400   GIOExtensionPoint *ep;
401   GIOExtension *extension;
402   GList *l;
403
404   native_class_out = data;
405   
406   use_this = g_getenv ("GIO_USE_VOLUME_MONITOR");
407   
408   /* Ensure vfs in modules loaded */
409   _g_io_modules_ensure_loaded ();
410
411   ep = g_io_extension_point_lookup (G_NATIVE_VOLUME_MONITOR_EXTENSION_POINT_NAME);
412
413   native_class = NULL;
414   if (use_this)
415     {
416       extension = g_io_extension_point_get_extension_by_name (ep, use_this);
417       if (extension)
418         {
419           klass = G_NATIVE_VOLUME_MONITOR_CLASS (g_io_extension_ref_class (extension));
420           if (G_VOLUME_MONITOR_CLASS (klass)->is_supported())
421             native_class = klass;
422           else
423             g_type_class_unref (klass);
424         }
425     }
426
427   if (native_class == NULL)
428     {
429       for (l = g_io_extension_point_get_extensions (ep); l != NULL; l = l->next)
430         {
431           extension = l->data;
432           klass = G_NATIVE_VOLUME_MONITOR_CLASS (g_io_extension_ref_class (extension));
433           if (G_VOLUME_MONITOR_CLASS (klass)->is_supported())
434             {
435               native_class = klass;
436               break;
437             }
438           else
439             g_type_class_unref (klass);
440         }
441     }
442  
443   if (native_class)
444     {
445       *native_class_out = native_class;
446       return G_TYPE_FROM_CLASS (native_class);
447     }
448   else
449     return G_TYPE_INVALID;
450 }
451
452 /* We return the class, with a ref taken.
453  * This way we avoid unloading the class/module
454  * between selecting the type and creating the
455  * instance on the first call.
456  */
457 static GNativeVolumeMonitorClass *
458 get_native_class ()
459 {
460   static GOnce once_init = G_ONCE_INIT;
461   GTypeClass *type_class;
462
463   type_class = NULL;
464   g_once (&once_init, (GThreadFunc)get_default_native_class, &type_class);
465
466   if (type_class == NULL && once_init.retval != G_TYPE_INVALID)
467     type_class = g_type_class_ref ((GType)once_init.retval);
468   
469   return (GNativeVolumeMonitorClass *)type_class;
470 }
471
472 static void
473 g_union_volume_monitor_init (GUnionVolumeMonitor *union_monitor)
474 {
475 }
476
477 static void
478 populate_union_monitor (GUnionVolumeMonitor *union_monitor)
479 {
480   GVolumeMonitor *monitor;
481   GNativeVolumeMonitorClass *native_class;
482   GVolumeMonitorClass *klass;
483   GIOExtensionPoint *ep;
484   GIOExtension *extension;
485   GList *l;
486
487   native_class = get_native_class ();
488
489   if (native_class != NULL)
490     {
491       monitor = g_object_new (G_TYPE_FROM_CLASS (native_class), NULL);
492       g_union_volume_monitor_add_monitor (union_monitor, monitor);
493       g_object_unref (monitor);
494       g_type_class_unref (native_class);
495     }
496
497   ep = g_io_extension_point_lookup (G_VOLUME_MONITOR_EXTENSION_POINT_NAME);
498   for (l = g_io_extension_point_get_extensions (ep); l != NULL; l = l->next)
499     {
500       extension = l->data;
501       
502       klass = G_VOLUME_MONITOR_CLASS (g_io_extension_ref_class (extension));
503       if (klass->is_supported == NULL || klass->is_supported())
504         {
505           monitor = g_object_new (g_io_extension_get_type (extension), NULL);
506           g_union_volume_monitor_add_monitor (union_monitor, monitor);
507           g_object_unref (monitor);
508         }
509       g_type_class_unref (klass);
510     }
511 }
512
513 static GUnionVolumeMonitor *
514 g_union_volume_monitor_new (void)
515 {
516   GUnionVolumeMonitor *monitor;
517
518   monitor = g_object_new (G_TYPE_UNION_VOLUME_MONITOR, NULL);
519   
520   return monitor;
521 }
522
523 /**
524  * g_volume_monitor_get:
525  * 
526  * Gets the volume monitor used by gio.
527  *
528  * Returns: a reference to the #GVolumeMonitor used by gio. Call
529  *    g_object_unref() when done with it.
530  **/
531 GVolumeMonitor *
532 g_volume_monitor_get (void)
533 {
534   GVolumeMonitor *vm;
535   
536   g_static_rec_mutex_lock (&the_volume_monitor_mutex);
537
538   if (the_volume_monitor)
539     vm = G_VOLUME_MONITOR (g_object_ref (the_volume_monitor));
540   else
541     {
542       the_volume_monitor = g_union_volume_monitor_new ();
543       populate_union_monitor (the_volume_monitor);
544       vm = G_VOLUME_MONITOR (the_volume_monitor);
545     }
546   
547   g_static_rec_mutex_unlock (&the_volume_monitor_mutex);
548
549   return vm;
550 }
551
552 /**
553  * _g_mount_get_for_mount_path:
554  * @mountpoint: a string.
555  * @cancellable: a #GCancellable, or %NULL
556  * 
557  * Returns: a #GMount for given @mount_path or %NULL.  
558  **/
559 GMount *
560 _g_mount_get_for_mount_path (const char *mount_path,
561                              GCancellable *cancellable)
562 {
563   GNativeVolumeMonitorClass *klass;
564   GMount *mount;
565   
566   klass = get_native_class ();
567   if (klass == NULL)
568     return NULL;
569
570   mount = NULL;
571
572   if (klass->get_mount_for_mount_path)
573     {
574       g_static_rec_mutex_lock (&the_volume_monitor_mutex);
575       mount = klass->get_mount_for_mount_path (mount_path, cancellable);
576       g_static_rec_mutex_unlock (&the_volume_monitor_mutex);
577     }
578
579   /* TODO: How do we know this succeeded? Keep in mind that the native
580    *       volume monitor may fail (e.g. not being able to connect to
581    *       hald). Is the get_mount_for_mount_path() method allowed to
582    *       return NULL? Seems like it is ... probably the method needs
583    *       to take a boolean and write if it succeeds or not.. Messy.
584    *       Very messy.
585    */
586   
587   g_type_class_unref (klass);
588
589   return mount;
590 }
591
592 /**
593  * g_volume_monitor_adopt_orphan_mount:
594  * @mount: a #GMount object to find a parent for
595  *
596  * This function should be called by any #GVolumeMonitor
597  * implementation when a new #GMount object is created that is not
598  * associated with a #GVolume object. It must be called just before
599  * emitting the @mount_added signal.
600  *
601  * If the return value is not %NULL, the caller must associate the
602  * returned #GVolume object with the #GMount. This involves returning
603  * it in it's g_mount_get_volume() implementation. The caller must
604  * also listen for the "removed" signal on the returned object
605  * and give up it's reference when handling that signal
606  * 
607  * Similary, if implementing g_volume_monitor_adopt_orphan_mount(),
608  * the implementor must take a reference to @mount and return it in
609  * it's g_volume_get_mount() implemented. Also, the implementor must
610  * listen for the "unmounted" signal on @mount and give up it's
611  * reference upon handling that signal.
612  *
613  * There are two main use cases for this function.
614  *
615  * One is when implementing a user space file system driver that reads
616  * blocks of a block device that is already represented by the native
617  * volume monitor (for example a CD Audio file system driver). Such
618  * a driver will generate it's own #GMount object that needs to be
619  * assoicated with the #GVolume object that represents the volume.
620  *
621  * The other is for implementing a #GVolumeMonitor whose sole purpose
622  * is to return #GVolume objects representing entries in the users
623  * "favorite servers" list or similar.
624  *
625  * Returns: the #GVolume object that is the parent for @mount or %NULL
626  * if no wants to adopt the #GMount.
627  */
628 GVolume *
629 g_volume_monitor_adopt_orphan_mount (GMount *mount)
630 {
631   GVolumeMonitor *child_monitor;
632   GVolumeMonitorClass *child_monitor_class;
633   GVolume *volume;
634   GList *l;
635
636   g_return_val_if_fail (mount != NULL, NULL);
637
638   if (the_volume_monitor == NULL)
639     return NULL;
640
641   volume = NULL;
642   
643   g_static_rec_mutex_lock (&the_volume_monitor_mutex);
644
645   for (l = the_volume_monitor->monitors; l != NULL; l = l->next)
646     {
647       child_monitor = l->data;
648       child_monitor_class = G_VOLUME_MONITOR_GET_CLASS (child_monitor);
649
650       if (child_monitor_class->adopt_orphan_mount != NULL)
651         {
652           volume = child_monitor_class->adopt_orphan_mount (mount);
653           if (volume != NULL)
654             break;
655         }
656     }
657   
658   g_static_rec_mutex_unlock (&the_volume_monitor_mutex);
659
660   return volume;
661 }
662
663
664 #define __G_UNION_VOLUME_MONITOR_C__
665 #include "gioaliasdef.c"