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