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