Rework how volumes, drives and volume monitoring is done. Previosly the
[platform/upstream/glib.git] / gio / gunionvolumemonitor.c
1 /* GIO - GLib Input, Output and Streaming Library
2  * 
3  * Copyright (C) 2006-2007 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Author: Alexander Larsson <alexl@redhat.com>
21  *         David Zeuthen <davidz@redhat.com>
22  */
23
24 #include <config.h>
25
26 #include <string.h>
27
28 #include <glib.h>
29 #include "gunionvolumemonitor.h"
30 #include "gmountprivate.h"
31 #include "giomodule-priv.h"
32 #ifdef G_OS_UNIX
33 #include "gunixvolumemonitor.h"
34 #endif
35 #include "gnativevolumemonitor.h"
36
37 #include "glibintl.h"
38
39 #include "gioalias.h"
40
41 struct _GUnionVolumeMonitor {
42   GVolumeMonitor parent;
43
44   GList *monitors;
45 };
46
47 static void g_union_volume_monitor_remove_monitor (GUnionVolumeMonitor *union_monitor,
48                                                    GVolumeMonitor *child_monitor);
49
50
51 #define g_union_volume_monitor_get_type _g_union_volume_monitor_get_type
52 G_DEFINE_TYPE (GUnionVolumeMonitor, g_union_volume_monitor, G_TYPE_VOLUME_MONITOR);
53
54
55 G_LOCK_DEFINE_STATIC(the_volume_monitor);
56 static GUnionVolumeMonitor *the_volume_monitor = NULL;
57
58 static void
59 g_union_volume_monitor_finalize (GObject *object)
60 {
61   GUnionVolumeMonitor *monitor;
62   
63   monitor = G_UNION_VOLUME_MONITOR (object);
64
65   while (monitor->monitors != NULL)
66     g_union_volume_monitor_remove_monitor (monitor,
67                                            monitor->monitors->data);
68   
69   if (G_OBJECT_CLASS (g_union_volume_monitor_parent_class)->finalize)
70     (*G_OBJECT_CLASS (g_union_volume_monitor_parent_class)->finalize) (object);
71 }
72
73 static void
74 g_union_volume_monitor_dispose (GObject *object)
75 {
76   GUnionVolumeMonitor *monitor;
77   
78   monitor = G_UNION_VOLUME_MONITOR (object);
79
80   G_LOCK (the_volume_monitor);
81   the_volume_monitor = NULL;
82   G_UNLOCK (the_volume_monitor);
83   
84   if (G_OBJECT_CLASS (g_union_volume_monitor_parent_class)->dispose)
85     (*G_OBJECT_CLASS (g_union_volume_monitor_parent_class)->dispose) (object);
86 }
87
88 static GList *
89 get_mounts (GVolumeMonitor *volume_monitor)
90 {
91   GUnionVolumeMonitor *monitor;
92   GVolumeMonitor *child_monitor;
93   GList *res;
94   GList *l;
95   
96   monitor = G_UNION_VOLUME_MONITOR (volume_monitor);
97
98   res = NULL;
99   
100   G_LOCK (the_volume_monitor);
101
102   for (l = monitor->monitors; l != NULL; l = l->next)
103     {
104       child_monitor = l->data;
105
106       res = g_list_concat (res, g_volume_monitor_get_mounts (child_monitor));
107     }
108   
109   G_UNLOCK (the_volume_monitor);
110
111   return res;
112 }
113
114 static GList *
115 get_volumes (GVolumeMonitor *volume_monitor)
116 {
117   GUnionVolumeMonitor *monitor;
118   GVolumeMonitor *child_monitor;
119   GList *res;
120   GList *l;
121   
122   monitor = G_UNION_VOLUME_MONITOR (volume_monitor);
123
124   res = NULL;
125   
126   G_LOCK (the_volume_monitor);
127
128   for (l = monitor->monitors; l != NULL; l = l->next)
129     {
130       child_monitor = l->data;
131
132       res = g_list_concat (res, g_volume_monitor_get_volumes (child_monitor));
133     }
134   
135   G_UNLOCK (the_volume_monitor);
136
137   return res;
138 }
139
140 static GList *
141 get_connected_drives (GVolumeMonitor *volume_monitor)
142 {
143   GUnionVolumeMonitor *monitor;
144   GVolumeMonitor *child_monitor;
145   GList *res;
146   GList *l;
147   
148   monitor = G_UNION_VOLUME_MONITOR (volume_monitor);
149
150   res = NULL;
151   
152   G_LOCK (the_volume_monitor);
153
154   for (l = monitor->monitors; l != NULL; l = l->next)
155     {
156       child_monitor = l->data;
157
158       res = g_list_concat (res, g_volume_monitor_get_connected_drives (child_monitor));
159     }
160   
161   G_UNLOCK (the_volume_monitor);
162
163   return res;
164 }
165
166 static void
167 g_union_volume_monitor_class_init (GUnionVolumeMonitorClass *klass)
168 {
169   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
170   GVolumeMonitorClass *monitor_class = G_VOLUME_MONITOR_CLASS (klass);
171   
172   gobject_class->finalize = g_union_volume_monitor_finalize;
173   gobject_class->dispose = g_union_volume_monitor_dispose;
174
175   monitor_class->get_connected_drives = get_connected_drives;
176   monitor_class->get_volumes = get_volumes;
177   monitor_class->get_mounts = get_mounts;
178 }
179
180 static void
181 child_volume_added (GVolumeMonitor      *child_monitor,
182                     GVolume    *child_volume,
183                     GUnionVolumeMonitor *union_monitor)
184 {
185   g_signal_emit_by_name (union_monitor,
186                          "volume_added",
187                          child_volume);
188 }
189
190 static void
191 child_volume_removed (GVolumeMonitor      *child_monitor,
192                       GVolume    *child_volume,
193                       GUnionVolumeMonitor *union_monitor)
194 {
195   g_signal_emit_by_name (union_monitor,
196                          "volume_removed",
197                          child_volume);
198 }
199
200 static void
201 child_volume_changed (GVolumeMonitor      *child_monitor,
202                       GVolume    *child_volume,
203                       GUnionVolumeMonitor *union_monitor)
204 {
205   g_signal_emit_by_name (union_monitor,
206                          "volume_changed",
207                          child_volume);
208 }
209
210 static void
211 child_mount_added (GVolumeMonitor      *child_monitor,
212                    GMount              *child_mount,
213                    GUnionVolumeMonitor *union_monitor)
214 {
215   g_signal_emit_by_name (union_monitor,
216                          "mount_added",
217                          child_mount);
218 }
219
220 static void
221 child_mount_removed (GVolumeMonitor      *child_monitor,
222                      GMount              *child_mount,
223                      GUnionVolumeMonitor *union_monitor)
224 {
225   g_signal_emit_by_name (union_monitor,
226                          "mount_removed",
227                          child_mount);
228 }
229
230 static void
231 child_mount_pre_unmount (GVolumeMonitor       *child_monitor,
232                           GMount              *child_mount,
233                           GUnionVolumeMonitor *union_monitor)
234 {
235   g_signal_emit_by_name (union_monitor,
236                          "mount_pre_unmount",
237                          child_mount);
238 }
239
240
241 static void
242 child_mount_changed (GVolumeMonitor       *child_monitor,
243                       GMount              *child_mount,
244                       GUnionVolumeMonitor *union_monitor)
245 {
246   g_signal_emit_by_name (union_monitor,
247                          "mount_changed",
248                          child_mount);
249 }
250
251 static void
252 child_drive_connected (GVolumeMonitor      *child_monitor,
253                        GDrive              *child_drive,
254                        GUnionVolumeMonitor *union_monitor)
255 {
256   g_signal_emit_by_name (union_monitor,
257                          "drive_connected",
258                          child_drive);
259 }
260
261 static void
262 child_drive_disconnected (GVolumeMonitor      *child_monitor,
263                           GDrive              *child_drive,
264                           GUnionVolumeMonitor *union_monitor)
265 {
266   g_signal_emit_by_name (union_monitor,
267                          "drive_disconnected",
268                          child_drive);
269 }
270
271 static void
272 child_drive_changed (GVolumeMonitor      *child_monitor,
273                      GDrive             *child_drive,
274                      GUnionVolumeMonitor *union_monitor)
275 {
276   g_signal_emit_by_name (union_monitor,
277                          "drive_changed",
278                          child_drive);
279 }
280
281 static void
282 g_union_volume_monitor_add_monitor (GUnionVolumeMonitor *union_monitor,
283                                     GVolumeMonitor      *volume_monitor)
284 {
285   if (g_list_find (union_monitor->monitors, volume_monitor))
286     return;
287
288   union_monitor->monitors =
289     g_list_prepend (union_monitor->monitors,
290                     g_object_ref (volume_monitor));
291
292   g_signal_connect (volume_monitor, "volume_added", (GCallback)child_volume_added, union_monitor);
293   g_signal_connect (volume_monitor, "volume_removed", (GCallback)child_volume_removed, union_monitor);
294   g_signal_connect (volume_monitor, "volume_changed", (GCallback)child_volume_changed, union_monitor);
295   g_signal_connect (volume_monitor, "mount_added", (GCallback)child_mount_added, union_monitor);
296   g_signal_connect (volume_monitor, "mount_removed", (GCallback)child_mount_removed, union_monitor);
297   g_signal_connect (volume_monitor, "mount_pre_unmount", (GCallback)child_mount_pre_unmount, union_monitor);
298   g_signal_connect (volume_monitor, "mount_changed", (GCallback)child_mount_changed, union_monitor);
299   g_signal_connect (volume_monitor, "drive_connected", (GCallback)child_drive_connected, union_monitor);
300   g_signal_connect (volume_monitor, "drive_disconnected", (GCallback)child_drive_disconnected, union_monitor);
301   g_signal_connect (volume_monitor, "drive_changed", (GCallback)child_drive_changed, union_monitor);
302 }
303
304 static void
305 g_union_volume_monitor_remove_monitor (GUnionVolumeMonitor *union_monitor,
306                                        GVolumeMonitor      *child_monitor)
307 {
308   GList *l;
309
310   l = g_list_find (union_monitor->monitors, child_monitor);
311   if (l == NULL)
312     return;
313
314   union_monitor->monitors = g_list_delete_link (union_monitor->monitors, l);
315
316   g_signal_handlers_disconnect_by_func (child_monitor, child_volume_added, union_monitor);
317   g_signal_handlers_disconnect_by_func (child_monitor, child_volume_removed, union_monitor);
318   g_signal_handlers_disconnect_by_func (child_monitor, child_volume_changed, union_monitor);
319   g_signal_handlers_disconnect_by_func (child_monitor, child_mount_added, union_monitor);
320   g_signal_handlers_disconnect_by_func (child_monitor, child_mount_removed, union_monitor);
321   g_signal_handlers_disconnect_by_func (child_monitor, child_mount_pre_unmount, union_monitor);
322   g_signal_handlers_disconnect_by_func (child_monitor, child_mount_changed, union_monitor);
323   g_signal_handlers_disconnect_by_func (child_monitor, child_drive_connected, union_monitor);
324   g_signal_handlers_disconnect_by_func (child_monitor, child_drive_disconnected, union_monitor);
325   g_signal_handlers_disconnect_by_func (child_monitor, child_drive_changed, union_monitor);
326 }
327
328 static gpointer
329 get_default_native_type (gpointer data)
330 {
331   GNativeVolumeMonitorClass *klass;
332   GType *monitors;
333   guint n_monitors;
334   GType native_type;
335   GType *ret = (GType *) data;
336   int native_prio;
337   int i;
338       
339 #ifdef G_OS_UNIX
340   /* Ensure GUnixVolumeMonitor type is available */
341   {
342     volatile GType unix_type;
343     /* volatile is required to avoid any G_GNUC_CONST optimizations */
344     unix_type = _g_unix_volume_monitor_get_type ();
345   }
346 #endif
347       
348   /* Ensure vfs in modules loaded */
349   _g_io_modules_ensure_loaded ();
350       
351   monitors = g_type_children (G_TYPE_NATIVE_VOLUME_MONITOR, &n_monitors);
352   native_type = 0;
353   native_prio = -1;
354   
355   for (i = 0; i < n_monitors; i++)
356     {
357       klass = G_NATIVE_VOLUME_MONITOR_CLASS (g_type_class_ref (monitors[i]));
358       if (klass->priority > native_prio)
359         {
360           native_prio = klass->priority;
361           native_type = monitors[i];
362         }
363
364       g_type_class_unref (klass);
365     }
366       
367   g_free (monitors);
368
369   *ret = native_type;
370
371   return NULL;
372 }
373
374 static GType
375 get_native_type (void)
376 {
377   static GOnce once_init = G_ONCE_INIT;
378   static GType type = G_TYPE_INVALID;
379
380   g_once (&once_init, get_default_native_type, &type);
381   
382   return type;
383 }
384
385 static void
386 g_union_volume_monitor_init (GUnionVolumeMonitor *union_monitor)
387 {
388   GVolumeMonitor *monitor;
389   GType *monitors;
390   guint n_monitors;
391   GType native_type;
392   int i;
393
394   native_type = get_native_type ();
395
396   if (native_type != G_TYPE_INVALID)
397     {
398       monitor = g_object_new (native_type, NULL);
399       g_union_volume_monitor_add_monitor (union_monitor, monitor);
400       g_object_unref (monitor);
401     }
402   
403   monitors = g_type_children (G_TYPE_VOLUME_MONITOR, &n_monitors);
404   
405   for (i = 0; i < n_monitors; i++)
406     {
407       if (monitors[i] == G_TYPE_UNION_VOLUME_MONITOR ||
408           g_type_is_a (monitors[i], G_TYPE_NATIVE_VOLUME_MONITOR))
409         continue;
410       
411       monitor = g_object_new (monitors[i], NULL);
412       g_union_volume_monitor_add_monitor (union_monitor, monitor);
413       g_object_unref (monitor);
414     }
415       
416   g_free (monitors);
417 }
418
419 static GUnionVolumeMonitor *
420 g_union_volume_monitor_new (void)
421 {
422   GUnionVolumeMonitor *monitor;
423
424   monitor = g_object_new (G_TYPE_UNION_VOLUME_MONITOR, NULL);
425   
426   return monitor;
427 }
428
429
430 /**
431  * g_volume_monitor_get:
432  * 
433  * Gets the volume monitor used by gio.
434  *
435  * Returns: a reference to the #GVolumeMonitor used by gio. Call
436  *    g_object_unref() when done with it.
437  **/
438 GVolumeMonitor *
439 g_volume_monitor_get (void)
440 {
441   GVolumeMonitor *vm;
442   
443   G_LOCK (the_volume_monitor);
444
445   if (the_volume_monitor)
446     vm = G_VOLUME_MONITOR (g_object_ref (the_volume_monitor));
447   else
448     {
449       the_volume_monitor = g_union_volume_monitor_new ();
450       vm = G_VOLUME_MONITOR (the_volume_monitor);
451     }
452   
453   G_UNLOCK (the_volume_monitor);
454
455   return vm;
456 }
457
458 /**
459  * _g_mount_get_for_mount_path:
460  * @mountpoint: a string.
461  * 
462  * Returns: a #GMount for given @mount_path or %NULL.  
463  **/
464 GMount *
465 _g_mount_get_for_mount_path (const char *mount_path)
466 {
467   GType native_type;
468   GNativeVolumeMonitorClass *klass;
469   GMount *mount;
470   
471   native_type = get_native_type ();
472
473   if (native_type == G_TYPE_INVALID)
474     return NULL;
475
476   mount = NULL;
477   
478   klass = G_NATIVE_VOLUME_MONITOR_CLASS (g_type_class_ref (native_type));
479   if (klass->get_mount_for_mount_path)
480     mount = klass->get_mount_for_mount_path (mount_path);
481   
482   g_type_class_unref (klass);
483
484   return mount;
485 }
486
487 #define __G_UNION_VOLUME_MONITOR_C__
488 #include "gioaliasdef.c"