Branch data Line data Source code
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 : : * SPDX-License-Identifier: LGPL-2.1-or-later
8 : : *
9 : : * This library is free software; you can redistribute it and/or
10 : : * modify it under the terms of the GNU Lesser General Public
11 : : * License as published by the Free Software Foundation; either
12 : : * version 2.1 of the License, or (at your option) any later version.
13 : : *
14 : : * This library is distributed in the hope that it will be useful,
15 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 : : * Lesser General Public License for more details.
18 : : *
19 : : * You should have received a copy of the GNU Lesser General
20 : : * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
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 : 244 : 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 : 1 : g_unix_volume_monitor_finalize (GObject *object)
69 : : {
70 : : GUnixVolumeMonitor *monitor;
71 : :
72 : 1 : monitor = G_UNIX_VOLUME_MONITOR (object);
73 : :
74 : 1 : g_signal_handlers_disconnect_by_func (monitor->mount_monitor, mountpoints_changed, monitor);
75 : 1 : g_signal_handlers_disconnect_by_func (monitor->mount_monitor, mounts_changed, monitor);
76 : :
77 : 1 : g_object_unref (monitor->mount_monitor);
78 : :
79 : 1 : g_list_free_full (monitor->last_mountpoints, (GDestroyNotify) g_unix_mount_point_free);
80 : 1 : g_list_free_full (monitor->last_mounts, (GDestroyNotify) g_unix_mount_entry_free);
81 : :
82 : 1 : g_list_free_full (monitor->volumes, g_object_unref);
83 : 1 : g_list_free_full (monitor->mounts, g_object_unref);
84 : :
85 : 1 : G_OBJECT_CLASS (g_unix_volume_monitor_parent_class)->finalize (object);
86 : 1 : }
87 : :
88 : : static void
89 : 2 : g_unix_volume_monitor_dispose (GObject *object)
90 : : {
91 : : GUnixVolumeMonitor *monitor;
92 : :
93 : 2 : monitor = G_UNIX_VOLUME_MONITOR (object);
94 : :
95 : 2 : g_list_free_full (monitor->volumes, g_object_unref);
96 : 2 : monitor->volumes = NULL;
97 : :
98 : 2 : g_list_free_full (monitor->mounts, g_object_unref);
99 : 2 : monitor->mounts = NULL;
100 : :
101 : 2 : G_OBJECT_CLASS (g_unix_volume_monitor_parent_class)->dispose (object);
102 : 2 : }
103 : :
104 : : static GList *
105 : 1 : get_mounts (GVolumeMonitor *volume_monitor)
106 : : {
107 : : GUnixVolumeMonitor *monitor;
108 : :
109 : 1 : monitor = G_UNIX_VOLUME_MONITOR (volume_monitor);
110 : :
111 : 1 : return g_list_copy_deep (monitor->mounts, (GCopyFunc) g_object_ref, NULL);
112 : : }
113 : :
114 : : static GList *
115 : 1 : get_volumes (GVolumeMonitor *volume_monitor)
116 : : {
117 : : GUnixVolumeMonitor *monitor;
118 : :
119 : 1 : monitor = G_UNIX_VOLUME_MONITOR (volume_monitor);
120 : :
121 : 1 : return g_list_copy_deep (monitor->volumes, (GCopyFunc) g_object_ref, NULL);
122 : : }
123 : :
124 : : static GList *
125 : 1 : get_connected_drives (GVolumeMonitor *volume_monitor)
126 : : {
127 : 1 : return NULL;
128 : : }
129 : :
130 : : static GVolume *
131 : 0 : get_volume_for_uuid (GVolumeMonitor *volume_monitor, const char *uuid)
132 : : {
133 : 0 : return NULL;
134 : : }
135 : :
136 : : static GMount *
137 : 0 : get_mount_for_uuid (GVolumeMonitor *volume_monitor, const char *uuid)
138 : : {
139 : 0 : return NULL;
140 : : }
141 : :
142 : : static gboolean
143 : 1 : is_supported (void)
144 : : {
145 : 1 : return TRUE;
146 : : }
147 : :
148 : : static GMount *
149 : 0 : get_mount_for_mount_path (const char *mount_path,
150 : : GCancellable *cancellable)
151 : : {
152 : : GUnixMountEntry *mount_entry;
153 : : GUnixMount *mount;
154 : :
155 : 0 : mount_entry = g_unix_mount_entry_at (mount_path, NULL);
156 : :
157 : 0 : if (!mount_entry)
158 : 0 : return NULL;
159 : :
160 : : /* TODO: Set mountable volume? */
161 : 0 : mount = _g_unix_mount_new (NULL, mount_entry, NULL);
162 : :
163 : 0 : g_unix_mount_entry_free (mount_entry);
164 : :
165 : 0 : return G_MOUNT (mount);
166 : : }
167 : :
168 : : static void
169 : 1 : g_unix_volume_monitor_class_init (GUnixVolumeMonitorClass *klass)
170 : : {
171 : 1 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
172 : 1 : GVolumeMonitorClass *monitor_class = G_VOLUME_MONITOR_CLASS (klass);
173 : 1 : GNativeVolumeMonitorClass *native_class = G_NATIVE_VOLUME_MONITOR_CLASS (klass);
174 : :
175 : 1 : gobject_class->finalize = g_unix_volume_monitor_finalize;
176 : 1 : gobject_class->dispose = g_unix_volume_monitor_dispose;
177 : :
178 : 1 : monitor_class->get_mounts = get_mounts;
179 : 1 : monitor_class->get_volumes = get_volumes;
180 : 1 : monitor_class->get_connected_drives = get_connected_drives;
181 : 1 : monitor_class->get_volume_for_uuid = get_volume_for_uuid;
182 : 1 : monitor_class->get_mount_for_uuid = get_mount_for_uuid;
183 : 1 : monitor_class->is_supported = is_supported;
184 : :
185 : 1 : native_class->get_mount_for_mount_path = get_mount_for_mount_path;
186 : 1 : }
187 : :
188 : : void
189 : 1 : _g_unix_volume_monitor_update (GUnixVolumeMonitor *unix_monitor)
190 : : {
191 : : /* Update both to make sure volumes are created before mounts */
192 : 1 : update_volumes (unix_monitor);
193 : 1 : update_mounts (unix_monitor);
194 : 1 : }
195 : :
196 : : static void
197 : 0 : mountpoints_changed (GUnixMountMonitor *mount_monitor,
198 : : gpointer user_data)
199 : : {
200 : 0 : GUnixVolumeMonitor *unix_monitor = user_data;
201 : :
202 : 0 : _g_unix_volume_monitor_update (unix_monitor);
203 : 0 : }
204 : :
205 : : static void
206 : 0 : mounts_changed (GUnixMountMonitor *mount_monitor,
207 : : gpointer user_data)
208 : : {
209 : 0 : GUnixVolumeMonitor *unix_monitor = user_data;
210 : :
211 : 0 : _g_unix_volume_monitor_update (unix_monitor);
212 : 0 : }
213 : :
214 : : static void
215 : 1 : g_unix_volume_monitor_init (GUnixVolumeMonitor *unix_monitor)
216 : : {
217 : :
218 : 1 : unix_monitor->mount_monitor = g_unix_mount_monitor_get ();
219 : :
220 : 1 : g_signal_connect (unix_monitor->mount_monitor,
221 : : "mounts-changed", G_CALLBACK (mounts_changed),
222 : : unix_monitor);
223 : :
224 : 1 : g_signal_connect (unix_monitor->mount_monitor,
225 : : "mountpoints-changed", G_CALLBACK (mountpoints_changed),
226 : : unix_monitor);
227 : :
228 : 1 : _g_unix_volume_monitor_update (unix_monitor);
229 : 1 : }
230 : :
231 : : GVolumeMonitor *
232 : 0 : _g_unix_volume_monitor_new (void)
233 : : {
234 : : GUnixVolumeMonitor *monitor;
235 : :
236 : 0 : monitor = g_object_new (G_TYPE_UNIX_VOLUME_MONITOR, NULL);
237 : :
238 : 0 : return G_VOLUME_MONITOR (monitor);
239 : : }
240 : :
241 : : static void
242 : 2 : diff_sorted_lists (GList *list1,
243 : : GList *list2,
244 : : GCompareFunc compare,
245 : : GList **added,
246 : : GList **removed)
247 : : {
248 : : int order;
249 : :
250 : 2 : *added = *removed = NULL;
251 : :
252 : 2 : while (list1 != NULL &&
253 : : list2 != NULL)
254 : : {
255 : 0 : order = (*compare) (list1->data, list2->data);
256 : 0 : if (order < 0)
257 : : {
258 : 0 : *removed = g_list_prepend (*removed, list1->data);
259 : 0 : list1 = list1->next;
260 : : }
261 : 0 : else if (order > 0)
262 : : {
263 : 0 : *added = g_list_prepend (*added, list2->data);
264 : 0 : list2 = list2->next;
265 : : }
266 : : else
267 : : { /* same item */
268 : 0 : list1 = list1->next;
269 : 0 : list2 = list2->next;
270 : : }
271 : : }
272 : :
273 : 2 : while (list1 != NULL)
274 : : {
275 : 0 : *removed = g_list_prepend (*removed, list1->data);
276 : 0 : list1 = list1->next;
277 : : }
278 : 41 : while (list2 != NULL)
279 : : {
280 : 39 : *added = g_list_prepend (*added, list2->data);
281 : 39 : list2 = list2->next;
282 : : }
283 : 2 : }
284 : :
285 : : GUnixVolume *
286 : 39 : _g_unix_volume_monitor_lookup_volume_for_mount_path (GUnixVolumeMonitor *monitor,
287 : : const char *mount_path)
288 : : {
289 : : GList *l;
290 : :
291 : 39 : for (l = monitor->volumes; l != NULL; l = l->next)
292 : : {
293 : 0 : GUnixVolume *volume = l->data;
294 : :
295 : 0 : if (_g_unix_volume_has_mount_path (volume, mount_path))
296 : 0 : return volume;
297 : : }
298 : :
299 : 39 : return NULL;
300 : : }
301 : :
302 : : static GUnixMount *
303 : 0 : find_mount_by_mountpath (GUnixVolumeMonitor *monitor,
304 : : const char *mount_path)
305 : : {
306 : : GList *l;
307 : :
308 : 0 : for (l = monitor->mounts; l != NULL; l = l->next)
309 : : {
310 : 0 : GUnixMount *mount = l->data;
311 : :
312 : 0 : if (_g_unix_mount_has_mount_path (mount, mount_path))
313 : 0 : return mount;
314 : : }
315 : :
316 : 0 : return NULL;
317 : : }
318 : :
319 : : static void
320 : 1 : update_volumes (GUnixVolumeMonitor *monitor)
321 : : {
322 : : GList *new_mountpoints;
323 : : GList *removed, *added;
324 : : GList *l;
325 : : GUnixVolume *volume;
326 : :
327 : 1 : new_mountpoints = g_unix_mount_points_get (NULL);
328 : :
329 : 1 : new_mountpoints = g_list_sort (new_mountpoints, (GCompareFunc) g_unix_mount_point_compare);
330 : :
331 : 1 : diff_sorted_lists (monitor->last_mountpoints,
332 : : new_mountpoints, (GCompareFunc) g_unix_mount_point_compare,
333 : : &added, &removed);
334 : :
335 : 1 : for (l = removed; l != NULL; l = l->next)
336 : : {
337 : 0 : GUnixMountPoint *mountpoint = l->data;
338 : :
339 : 0 : volume = _g_unix_volume_monitor_lookup_volume_for_mount_path (monitor,
340 : : g_unix_mount_point_get_mount_path (mountpoint));
341 : 0 : if (volume)
342 : : {
343 : 0 : _g_unix_volume_disconnected (volume);
344 : 0 : monitor->volumes = g_list_remove (monitor->volumes, volume);
345 : 0 : g_signal_emit_by_name (monitor, "volume-removed", volume);
346 : 0 : g_signal_emit_by_name (volume, "removed");
347 : 0 : g_object_unref (volume);
348 : : }
349 : : }
350 : :
351 : 1 : for (l = added; l != NULL; l = l->next)
352 : : {
353 : 0 : GUnixMountPoint *mountpoint = l->data;
354 : :
355 : 0 : volume = _g_unix_volume_new (G_VOLUME_MONITOR (monitor), mountpoint);
356 : 0 : if (volume)
357 : : {
358 : 0 : monitor->volumes = g_list_prepend (monitor->volumes, volume);
359 : 0 : g_signal_emit_by_name (monitor, "volume-added", volume);
360 : : }
361 : : }
362 : :
363 : 1 : g_list_free (added);
364 : 1 : g_list_free (removed);
365 : 1 : g_list_free_full (monitor->last_mountpoints, (GDestroyNotify) g_unix_mount_point_free);
366 : 1 : monitor->last_mountpoints = new_mountpoints;
367 : 1 : }
368 : :
369 : : static void
370 : 1 : update_mounts (GUnixVolumeMonitor *monitor)
371 : : {
372 : : GList *new_mounts;
373 : : GList *removed, *added;
374 : : GList *l;
375 : : GUnixMount *mount;
376 : : GUnixVolume *volume;
377 : : const char *mount_path;
378 : :
379 : 1 : new_mounts = g_unix_mount_entries_get (NULL);
380 : :
381 : 1 : new_mounts = g_list_sort (new_mounts, (GCompareFunc) g_unix_mount_entry_compare);
382 : :
383 : 1 : diff_sorted_lists (monitor->last_mounts,
384 : : new_mounts, (GCompareFunc) g_unix_mount_entry_compare,
385 : : &added, &removed);
386 : :
387 : 1 : for (l = removed; l != NULL; l = l->next)
388 : : {
389 : 0 : GUnixMountEntry *mount_entry = l->data;
390 : :
391 : 0 : mount = find_mount_by_mountpath (monitor, g_unix_mount_entry_get_mount_path (mount_entry));
392 : 0 : if (mount)
393 : : {
394 : 0 : _g_unix_mount_unmounted (mount);
395 : 0 : monitor->mounts = g_list_remove (monitor->mounts, mount);
396 : 0 : g_signal_emit_by_name (monitor, "mount-removed", mount);
397 : 0 : g_signal_emit_by_name (mount, "unmounted");
398 : 0 : g_object_unref (mount);
399 : : }
400 : : }
401 : :
402 : 40 : for (l = added; l != NULL; l = l->next)
403 : : {
404 : 39 : GUnixMountEntry *mount_entry = l->data;
405 : :
406 : 39 : mount_path = g_unix_mount_entry_get_mount_path (mount_entry);
407 : :
408 : 39 : volume = _g_unix_volume_monitor_lookup_volume_for_mount_path (monitor, mount_path);
409 : 39 : mount = _g_unix_mount_new (G_VOLUME_MONITOR (monitor), mount_entry, volume);
410 : 39 : if (mount)
411 : : {
412 : 0 : monitor->mounts = g_list_prepend (monitor->mounts, mount);
413 : 0 : g_signal_emit_by_name (monitor, "mount-added", mount);
414 : : }
415 : : }
416 : :
417 : 1 : g_list_free (added);
418 : 1 : g_list_free (removed);
419 : 1 : g_list_free_full (monitor->last_mounts, (GDestroyNotify) g_unix_mount_entry_free);
420 : 1 : monitor->last_mounts = new_mounts;
421 : 1 : }
|