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