92ed3058ed2b98d8549cc879a0a67d1874497234
[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   GVolumeMonitor *child_monitor;
63
64   monitor = G_UNION_VOLUME_MONITOR (object);
65
66   while (monitor->monitors != NULL) {
67     child_monitor = monitor->monitors->data;
68     g_union_volume_monitor_remove_monitor (monitor, 
69                                            child_monitor);
70     g_object_unref (child_monitor);
71   }
72
73   
74   if (G_OBJECT_CLASS (g_union_volume_monitor_parent_class)->finalize)
75     (*G_OBJECT_CLASS (g_union_volume_monitor_parent_class)->finalize) (object);
76 }
77
78 static void
79 g_union_volume_monitor_dispose (GObject *object)
80 {
81   GUnionVolumeMonitor *monitor;
82   
83   monitor = G_UNION_VOLUME_MONITOR (object);
84
85   G_LOCK (the_volume_monitor);
86   the_volume_monitor = NULL;
87   G_UNLOCK (the_volume_monitor);
88   
89   if (G_OBJECT_CLASS (g_union_volume_monitor_parent_class)->dispose)
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_LOCK (the_volume_monitor);
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_UNLOCK (the_volume_monitor);
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_LOCK (the_volume_monitor);
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_UNLOCK (the_volume_monitor);
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_LOCK (the_volume_monitor);
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_UNLOCK (the_volume_monitor);
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_LOCK (the_volume_monitor);
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_UNLOCK (the_volume_monitor);
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_LOCK (the_volume_monitor);
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_UNLOCK (the_volume_monitor);
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_type_with_exclude (GType type_to_exclude)
395 {
396   GNativeVolumeMonitorClass *klass;
397   GType *monitors;
398   guint n_monitors;
399   GType native_type;
400   int native_prio;
401   int i;
402       
403 #ifdef G_OS_UNIX
404   /* Ensure GUnixVolumeMonitor type is available */
405   {
406     volatile GType unix_type;
407     /* volatile is required to avoid any G_GNUC_CONST optimizations */
408     unix_type = _g_unix_volume_monitor_get_type ();
409   }
410 #endif
411       
412   /* Ensure vfs in modules loaded */
413   _g_io_modules_ensure_loaded ();
414
415   monitors = g_type_children (G_TYPE_NATIVE_VOLUME_MONITOR, &n_monitors);
416   native_type = 0;
417   native_prio = -1;
418   
419   for (i = 0; i < n_monitors; i++)
420     {
421       if (monitors[i] != type_to_exclude)
422         {
423           klass = G_NATIVE_VOLUME_MONITOR_CLASS (g_type_class_ref (monitors[i]));
424
425           if (klass->priority > native_prio)
426             {
427               native_prio = klass->priority;
428               native_type = monitors[i];
429             }
430
431           g_type_class_unref (klass);
432         }
433     }
434       
435   g_free (monitors);
436
437   return native_type;
438 }
439
440 static gpointer
441 get_default_native_type (gpointer data)
442 {
443   GType *ret = (GType *) data;
444
445   *ret = get_default_native_type_with_exclude (G_TYPE_INVALID);
446   return NULL;
447 }
448
449 static GOnce _once_init = G_ONCE_INIT;
450 static GType _type = G_TYPE_INVALID;
451
452 static GType
453 get_native_type ()
454 {
455
456   g_once (&_once_init, get_default_native_type, &_type);
457   
458   return _type;
459 }
460
461 static void
462 update_native_type (GType type)
463 {
464   _type = type;
465 }
466
467 static void
468 g_union_volume_monitor_init (GUnionVolumeMonitor *union_monitor)
469 {
470   GVolumeMonitor *monitor;
471   GType *monitors;
472   guint n_monitors;
473   GType native_type;
474   int i;
475
476   native_type = get_native_type ();
477
478   if (native_type != G_TYPE_INVALID)
479     {
480       monitor = g_object_new (native_type, NULL);
481       /* A native file monitor (the hal one if hald isn't running for
482        * example) may very well fail so handle falling back to the
483        * native one shipped with gio (e.g. GUnixVolumeMonitor)
484        */
485       if (monitor == NULL)
486         {
487           native_type = get_default_native_type_with_exclude (native_type);
488           monitor = g_object_new (native_type, NULL);
489         }
490
491       if (monitor != NULL)
492         {
493           g_union_volume_monitor_add_monitor (union_monitor, monitor);
494           g_object_unref (monitor);
495           update_native_type (native_type);
496         }
497     }
498   
499   monitors = g_type_children (G_TYPE_VOLUME_MONITOR, &n_monitors);
500   
501   for (i = 0; i < n_monitors; i++)
502     {
503       if (monitors[i] == G_TYPE_UNION_VOLUME_MONITOR ||
504           g_type_is_a (monitors[i], G_TYPE_NATIVE_VOLUME_MONITOR))
505         continue;
506       
507       monitor = g_object_new (monitors[i], NULL);
508       g_union_volume_monitor_add_monitor (union_monitor, monitor);
509       g_object_unref (monitor);
510     }
511       
512   g_free (monitors);
513 }
514
515 static GUnionVolumeMonitor *
516 g_union_volume_monitor_new (void)
517 {
518   GUnionVolumeMonitor *monitor;
519
520   monitor = g_object_new (G_TYPE_UNION_VOLUME_MONITOR, NULL);
521   
522   return monitor;
523 }
524
525
526 /**
527  * g_volume_monitor_get:
528  * 
529  * Gets the volume monitor used by gio.
530  *
531  * Returns: a reference to the #GVolumeMonitor used by gio. Call
532  *    g_object_unref() when done with it.
533  **/
534 GVolumeMonitor *
535 g_volume_monitor_get (void)
536 {
537   GVolumeMonitor *vm;
538   
539   G_LOCK (the_volume_monitor);
540
541   if (the_volume_monitor)
542     vm = G_VOLUME_MONITOR (g_object_ref (the_volume_monitor));
543   else
544     {
545       the_volume_monitor = g_union_volume_monitor_new ();
546       vm = G_VOLUME_MONITOR (the_volume_monitor);
547     }
548   
549   G_UNLOCK (the_volume_monitor);
550
551   return vm;
552 }
553
554 /**
555  * _g_mount_get_for_mount_path:
556  * @mountpoint: a string.
557  * 
558  * Returns: a #GMount for given @mount_path or %NULL.  
559  **/
560 GMount *
561 _g_mount_get_for_mount_path (const char *mount_path)
562 {
563   GType native_type;
564   GNativeVolumeMonitorClass *klass;
565   GMount *mount;
566   
567   native_type = get_native_type ();
568
569   if (native_type == G_TYPE_INVALID)
570     return NULL;
571
572   mount = NULL;
573
574   klass = G_NATIVE_VOLUME_MONITOR_CLASS (g_type_class_ref (native_type));
575   if (klass->get_mount_for_mount_path)
576     {
577       G_LOCK (the_volume_monitor);
578       mount = klass->get_mount_for_mount_path (mount_path);
579       G_UNLOCK (the_volume_monitor);
580     }
581
582   /* TODO: How do we know this succeeded? Keep in mind that the native
583    *       volume monitor may fail (e.g. not being able to connect to
584    *       hald). Is the get_mount_for_mount_path() method allowed to
585    *       return NULL? Seems like it is ... probably the method needs
586    *       to take a boolean and write if it succeeds or not.. Messy.
587    *       Very messy.
588    */
589   
590   g_type_class_unref (klass);
591
592   return mount;
593 }
594
595 #define __G_UNION_VOLUME_MONITOR_C__
596 #include "gioaliasdef.c"