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