2430e4bccd69cb6ee0495b6fe99c54c17af4ba98
[platform/upstream/glib.git] / gio / gunixvolumemonitor.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  */
22
23 #include <config.h>
24
25 #include <string.h>
26
27 #include <glib.h>
28 #include "gunixvolumemonitor.h"
29 #include "gunixmounts.h"
30 #include "gunixvolume.h"
31 #include "gunixdrive.h"
32 #include "gvolumeprivate.h"
33
34 #include "glibintl.h"
35
36 struct _GUnixVolumeMonitor {
37   GNativeVolumeMonitor parent;
38
39   GUnixMountMonitor *mount_monitor;
40
41   GList *last_mountpoints;
42   GList *last_mounts;
43
44   GList *drives;
45   GList *volumes;
46 };
47
48 static void mountpoints_changed (GUnixMountMonitor  *mount_monitor,
49                                  gpointer            user_data);
50 static void mounts_changed      (GUnixMountMonitor  *mount_monitor,
51                                  gpointer            user_data);
52 static void update_drives       (GUnixVolumeMonitor *monitor);
53 static void update_volumes      (GUnixVolumeMonitor *monitor);
54
55 G_DEFINE_TYPE (GUnixVolumeMonitor, g_unix_volume_monitor, G_TYPE_NATIVE_VOLUME_MONITOR);
56
57 static void
58 g_unix_volume_monitor_finalize (GObject *object)
59 {
60   GUnixVolumeMonitor *monitor;
61   
62   monitor = G_UNIX_VOLUME_MONITOR (object);
63
64   g_signal_handlers_disconnect_by_func (monitor->mount_monitor, mountpoints_changed, monitor);
65   g_signal_handlers_disconnect_by_func (monitor->mount_monitor, mounts_changed, monitor);
66                                         
67   g_object_unref (monitor->mount_monitor);
68
69   g_list_foreach (monitor->last_mounts, (GFunc)g_unix_mount_free, NULL);
70   g_list_free (monitor->last_mounts);
71
72   g_list_foreach (monitor->volumes, (GFunc)g_object_unref, NULL);
73   g_list_free (monitor->volumes);
74   g_list_foreach (monitor->drives, (GFunc)g_object_unref, NULL);
75   g_list_free (monitor->drives);
76   
77   if (G_OBJECT_CLASS (g_unix_volume_monitor_parent_class)->finalize)
78     (*G_OBJECT_CLASS (g_unix_volume_monitor_parent_class)->finalize) (object);
79 }
80
81 static GList *
82 get_mounted_volumes (GVolumeMonitor *volume_monitor)
83 {
84   GUnixVolumeMonitor *monitor;
85   GList *l;
86   
87   monitor = G_UNIX_VOLUME_MONITOR (volume_monitor);
88
89   l = g_list_copy (monitor->volumes);
90   g_list_foreach (l, (GFunc)g_object_ref, NULL);
91
92   return l;
93 }
94
95 static GList *
96 get_connected_drives (GVolumeMonitor *volume_monitor)
97 {
98   GUnixVolumeMonitor *monitor;
99   GList *l;
100   
101   monitor = G_UNIX_VOLUME_MONITOR (volume_monitor);
102
103   l = g_list_copy (monitor->drives);
104   g_list_foreach (l, (GFunc)g_object_ref, NULL);
105
106   return l;
107 }
108
109 static GVolume *
110 get_volume_for_mountpoint (const char *mountpoint)
111 {
112   GUnixMount *mount;
113   GUnixVolume *volume;
114
115   mount = g_get_unix_mount_at (mountpoint, NULL);
116   
117   /* TODO: Set drive? */
118   volume = g_unix_volume_new (mount, NULL);
119
120   return G_VOLUME (volume);
121 }
122
123 static void
124 g_unix_volume_monitor_class_init (GUnixVolumeMonitorClass *klass)
125 {
126   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
127   GVolumeMonitorClass *monitor_class = G_VOLUME_MONITOR_CLASS (klass);
128   GNativeVolumeMonitorClass *native_class = G_NATIVE_VOLUME_MONITOR_CLASS (klass);
129   
130   gobject_class->finalize = g_unix_volume_monitor_finalize;
131
132   monitor_class->get_mounted_volumes = get_mounted_volumes;
133   monitor_class->get_connected_drives = get_connected_drives;
134
135   native_class->priority = 0;
136   native_class->get_volume_for_mountpoint = get_volume_for_mountpoint;
137 }
138
139 static void
140 mountpoints_changed (GUnixMountMonitor *mount_monitor,
141                      gpointer user_data)
142 {
143   GUnixVolumeMonitor *unix_monitor = user_data;
144
145   /* Update both to make sure drives are created before volumes */
146   update_drives (unix_monitor);
147   update_volumes (unix_monitor);
148 }
149
150 static void
151 mounts_changed (GUnixMountMonitor *mount_monitor,
152                 gpointer user_data)
153 {
154   GUnixVolumeMonitor *unix_monitor = user_data;
155
156   /* Update both to make sure drives are created before volumes */
157   update_drives (unix_monitor);
158   update_volumes (unix_monitor);
159 }
160
161 static void
162 g_unix_volume_monitor_init (GUnixVolumeMonitor *unix_monitor)
163 {
164
165   unix_monitor->mount_monitor = g_unix_mount_monitor_new ();
166
167   g_signal_connect (unix_monitor->mount_monitor,
168                     "mounts_changed", G_CALLBACK (mounts_changed),
169                     unix_monitor);
170   
171   g_signal_connect (unix_monitor->mount_monitor,
172                     "mountpoints_changed", G_CALLBACK (mountpoints_changed),
173                     unix_monitor);
174                     
175   update_drives (unix_monitor);
176   update_volumes (unix_monitor);
177
178 }
179
180 /**
181  * g_unix_volume_monitor_new:
182  * 
183  * Returns:  a new #GVolumeMonitor.
184  **/
185 GVolumeMonitor *
186 g_unix_volume_monitor_new (void)
187 {
188   GUnixVolumeMonitor *monitor;
189
190   monitor = g_object_new (G_TYPE_UNIX_VOLUME_MONITOR, NULL);
191   
192   return G_VOLUME_MONITOR (monitor);
193 }
194
195 static void
196 diff_sorted_lists (GList *list1, GList *list2, GCompareFunc compare,
197                    GList **added, GList **removed)
198 {
199   int order;
200   
201   *added = *removed = NULL;
202   
203   while (list1 != NULL &&
204          list2 != NULL)
205     {
206       order = (*compare) (list1->data, list2->data);
207       if (order < 0)
208         {
209           *removed = g_list_prepend (*removed, list1->data);
210           list1 = list1->next;
211         }
212       else if (order > 0)
213         {
214           *added = g_list_prepend (*added, list2->data);
215           list2 = list2->next;
216         }
217       else
218         { /* same item */
219           list1 = list1->next;
220           list2 = list2->next;
221         }
222     }
223
224   while (list1 != NULL)
225     {
226       *removed = g_list_prepend (*removed, list1->data);
227       list1 = list1->next;
228     }
229   while (list2 != NULL)
230     {
231       *added = g_list_prepend (*added, list2->data);
232       list2 = list2->next;
233     }
234 }
235
236 /**
237  * g_unix_volume_lookup_drive_for_mountpoint: 
238  * @monitor:
239  * @mountpoint:
240  * 
241  * Returns:  #GUnixDrive for the given @mountpoint.
242  **/
243 GUnixDrive *
244 g_unix_volume_monitor_lookup_drive_for_mountpoint (GUnixVolumeMonitor *monitor,
245                                                    const char *mountpoint)
246 {
247   GList *l;
248
249   for (l = monitor->drives; l != NULL; l = l->next)
250     {
251       GUnixDrive *drive = l->data;
252
253       if (g_unix_drive_has_mountpoint (drive, mountpoint))
254         return drive;
255     }
256   
257   return NULL;
258 }
259
260 static GUnixVolume *
261 find_volume_by_mountpoint (GUnixVolumeMonitor *monitor,
262                            const char *mountpoint)
263 {
264   GList *l;
265
266   for (l = monitor->volumes; l != NULL; l = l->next)
267     {
268       GUnixVolume *volume = l->data;
269
270       if (g_unix_volume_has_mountpoint (volume, mountpoint))
271         return volume;
272     }
273   
274   return NULL;
275 }
276
277 static void
278 update_drives (GUnixVolumeMonitor *monitor)
279 {
280   GList *new_mountpoints;
281   GList *removed, *added;
282   GList *l;
283   GUnixDrive *drive;
284   
285   new_mountpoints = g_get_unix_mount_points (NULL);
286   
287   new_mountpoints = g_list_sort (new_mountpoints, (GCompareFunc) g_unix_mount_point_compare);
288   
289   diff_sorted_lists (monitor->last_mountpoints,
290                      new_mountpoints, (GCompareFunc) g_unix_mount_point_compare,
291                      &added, &removed);
292   
293   for (l = removed; l != NULL; l = l->next)
294     {
295       GUnixMountPoint *mountpoint = l->data;
296       
297       drive = g_unix_volume_monitor_lookup_drive_for_mountpoint (monitor,
298                                                                  g_unix_mount_point_get_mount_path (mountpoint));
299       if (drive)
300         {
301           g_unix_drive_disconnected (drive);
302           monitor->drives = g_list_remove (monitor->drives, drive);
303           g_signal_emit_by_name (monitor, "drive_disconnected", drive);
304           g_object_unref (drive);
305         }
306     }
307   
308   for (l = added; l != NULL; l = l->next)
309     {
310       GUnixMountPoint *mountpoint = l->data;
311       
312       drive = g_unix_drive_new (G_VOLUME_MONITOR (monitor), mountpoint);
313       if (drive)
314         {
315           monitor->drives = g_list_prepend (monitor->drives, drive);
316           g_signal_emit_by_name (monitor, "drive_connected", drive);
317         }
318     }
319   
320   g_list_free (added);
321   g_list_free (removed);
322   g_list_foreach (monitor->last_mountpoints,
323                   (GFunc)g_unix_mount_point_free, NULL);
324   g_list_free (monitor->last_mountpoints);
325   monitor->last_mountpoints = new_mountpoints;
326 }
327
328 static void
329 update_volumes (GUnixVolumeMonitor *monitor)
330 {
331   GList *new_mounts;
332   GList *removed, *added;
333   GList *l;
334   GUnixVolume *volume;
335   GUnixDrive *drive;
336   const char *mount_path;
337   
338   new_mounts = g_get_unix_mounts (NULL);
339   
340   new_mounts = g_list_sort (new_mounts, (GCompareFunc) g_unix_mount_compare);
341   
342   diff_sorted_lists (monitor->last_mounts,
343                      new_mounts, (GCompareFunc) g_unix_mount_compare,
344                      &added, &removed);
345   
346   for (l = removed; l != NULL; l = l->next)
347     {
348       GUnixMount *mount = l->data;
349       
350       volume = find_volume_by_mountpoint (monitor, g_unix_mount_get_mount_path (mount));
351       if (volume)
352         {
353           g_unix_volume_unmounted (volume);
354           monitor->volumes = g_list_remove (monitor->volumes, volume);
355           g_signal_emit_by_name (monitor, "volume_unmounted", volume);
356           g_object_unref (volume);
357         }
358     }
359   
360   for (l = added; l != NULL; l = l->next)
361     {
362       GUnixMount *mount = l->data;
363
364       mount_path = g_unix_mount_get_mount_path (mount);
365       
366       drive = g_unix_volume_monitor_lookup_drive_for_mountpoint (monitor,
367                                                                  mount_path);
368       volume = g_unix_volume_new (mount, drive);
369       if (volume)
370         {
371           monitor->volumes = g_list_prepend (monitor->volumes, volume);
372           g_signal_emit_by_name (monitor, "volume_mounted", volume);
373         }
374     }
375   
376   g_list_free (added);
377   g_list_free (removed);
378   g_list_foreach (monitor->last_mounts,
379                   (GFunc)g_unix_mount_free, NULL);
380   g_list_free (monitor->last_mounts);
381   monitor->last_mounts = new_mounts;
382 }