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., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 *
20 * Author: Alexander Larsson <alexl@redhat.com>
21 */
22
23 #include <config.h>
24
25 #include <string.h>
26
27 #include <gmounttracker.h>
28 #include <gvfsdaemonprotocol.h>
29 #include <gvfsdbus.h>
30
31 enum {
32 MOUNTED,
33 UNMOUNTED,
34 LAST_SIGNAL
35 };
36
37 enum {
38 PROP_0,
39 PROP_CONNECTION,
40 PROP_USER_VISIBLE_ONLY
41 };
42
43 /* TODO: Real P_() */
44 #define P_(_x) (_x)
45 #define I_(string) g_intern_static_string (string)
46
47 static guint signals[LAST_SIGNAL] = { 0 };
48
49 struct _GMountTracker
50 {
51 GObject parent_instance;
52
53 GMutex lock;
54 GList *mounts;
55 GDBusConnection *connection;
56 GVfsDBusMountTracker *proxy;
57
58 gboolean user_visible_only;
59 };
60
61 G_DEFINE_TYPE (GMountTracker, g_mount_tracker, G_TYPE_OBJECT)
62
63 static GObject* g_mount_tracker_constructor (GType type,
64 guint n_construct_properties,
65 GObjectConstructParam *construct_params);
66 static void g_mount_tracker_set_property (GObject *object,
67 guint prop_id,
68 const GValue *value,
69 GParamSpec *pspec);
70 static void g_mount_tracker_get_property (GObject *object,
71 guint prop_id,
72 GValue *value,
73 GParamSpec *pspec);
74
75 gboolean
g_mount_info_equal(GMountInfo * info1,GMountInfo * info2)76 g_mount_info_equal (GMountInfo *info1,
77 GMountInfo *info2)
78 {
79 return
80 strcmp (info1->dbus_id, info2->dbus_id) == 0 &&
81 strcmp (info1->object_path, info2->object_path) == 0;
82 }
83
84 GMountInfo *
g_mount_info_dup(GMountInfo * info)85 g_mount_info_dup (GMountInfo *info)
86 {
87 GMountInfo *copy;
88
89 copy = g_new (GMountInfo, 1);
90 copy->ref_count = 1;
91 copy->display_name = g_strdup (info->display_name);
92 copy->stable_name = g_strdup (info->stable_name);
93 copy->x_content_types = g_strdup (info->x_content_types);
94 copy->icon = g_object_ref (info->icon);
95 copy->symbolic_icon = g_object_ref (info->symbolic_icon);
96 copy->dbus_id = g_strdup (info->dbus_id);
97 copy->object_path = g_strdup (info->object_path);
98 copy->mount_spec = g_mount_spec_copy (info->mount_spec);
99 copy->user_visible = info->user_visible;
100 copy->prefered_filename_encoding = g_strdup (info->prefered_filename_encoding);
101 copy->fuse_mountpoint = g_strdup (info->fuse_mountpoint);
102 copy->default_location = g_strdup (info->default_location);
103
104 return copy;
105 }
106
107 GMountInfo *
g_mount_info_ref(GMountInfo * info)108 g_mount_info_ref (GMountInfo *info)
109 {
110 g_atomic_int_inc (&info->ref_count);
111 return info;
112 }
113
114 void
g_mount_info_unref(GMountInfo * info)115 g_mount_info_unref (GMountInfo *info)
116 {
117 if (g_atomic_int_dec_and_test (&info->ref_count))
118 {
119 g_free (info->display_name);
120 g_free (info->stable_name);
121 g_free (info->x_content_types);
122 g_object_unref (info->icon);
123 g_object_unref (info->symbolic_icon);
124 g_free (info->dbus_id);
125 g_free (info->object_path);
126 g_mount_spec_unref (info->mount_spec);
127 g_free (info->prefered_filename_encoding);
128 g_free (info->fuse_mountpoint);
129 g_free (info->default_location);
130 g_free (info);
131 }
132 }
133
134 const char *
g_mount_info_resolve_path(GMountInfo * info,const char * path)135 g_mount_info_resolve_path (GMountInfo *info,
136 const char *path)
137 {
138 const char *new_path;
139 int len;
140
141 if (info->mount_spec->mount_prefix != NULL &&
142 info->mount_spec->mount_prefix[0] != 0)
143 {
144 len = strlen (info->mount_spec->mount_prefix);
145 if (info->mount_spec->mount_prefix[len-1] == '/')
146 len--;
147 new_path = path + len;
148 }
149 else
150 new_path = path;
151
152 if (new_path == NULL ||
153 new_path[0] == 0)
154 new_path = "/";
155
156 return new_path;
157 }
158
159 void
g_mount_info_apply_prefix(GMountInfo * info,char ** path)160 g_mount_info_apply_prefix (GMountInfo *info,
161 char **path)
162 {
163 GMountSpec *spec;
164
165 spec = info->mount_spec;
166
167 if (spec->mount_prefix != NULL &&
168 spec->mount_prefix[0] != 0)
169 {
170 char *path_with_prefix;
171 path_with_prefix = g_build_path ("/", spec->mount_prefix,
172 *path, NULL);
173 g_free (*path);
174 *path = path_with_prefix;
175 }
176
177 }
178
179 GMountInfo *
g_mount_info_from_dbus(GVariant * value)180 g_mount_info_from_dbus (GVariant *value)
181 {
182 GMountInfo *info;
183 GMountSpec *mount_spec;
184 gboolean user_visible;
185 const gchar *display_name;
186 const gchar *stable_name;
187 const gchar *x_content_types;
188 const gchar *icon_str;
189 const gchar *symbolic_icon_str;
190 const gchar *prefered_filename_encoding;
191 const gchar *dbus_id;
192 const gchar *obj_path;
193 const gchar *fuse_mountpoint;
194 const gchar *default_location;
195 GIcon *icon;
196 GIcon *symbolic_icon;
197 GVariant *iter_mount_spec;
198 GError *error;
199
200 g_variant_get (value, "(&s&o&s&s&s&s&s&sb^&ay@(aya{sv})^&ay)",
201 &dbus_id,
202 &obj_path,
203 &display_name,
204 &stable_name,
205 &x_content_types,
206 &icon_str,
207 &symbolic_icon_str,
208 &prefered_filename_encoding,
209 &user_visible,
210 &fuse_mountpoint,
211 &iter_mount_spec,
212 &default_location);
213
214 mount_spec = g_mount_spec_from_dbus (iter_mount_spec);
215 g_variant_unref (iter_mount_spec);
216 if (mount_spec == NULL)
217 return NULL;
218
219 if (fuse_mountpoint && fuse_mountpoint[0] == '\0')
220 fuse_mountpoint = NULL;
221 if (default_location && default_location[0] == '\0')
222 default_location = NULL;
223
224 if (icon_str == NULL || strlen (icon_str) == 0)
225 icon_str = "drive-removable-media";
226 error = NULL;
227 icon = g_icon_new_for_string (icon_str, &error);
228 if (icon == NULL)
229 {
230 g_warning ("Malformed icon string '%s': %s", icon_str, error->message);
231 g_error_free (error);
232 icon = g_themed_icon_new ("gtk-missing-image"); /* TODO: maybe choose a better name */
233 }
234
235 if (symbolic_icon_str == NULL || strlen (symbolic_icon_str) == 0)
236 symbolic_icon_str = "drive-removable-media-symbolic";
237 error = NULL;
238 symbolic_icon = g_icon_new_for_string (symbolic_icon_str, &error);
239 if (symbolic_icon == NULL)
240 {
241 g_warning ("Malformed icon string '%s': %s", symbolic_icon_str, error->message);
242 g_error_free (error);
243 symbolic_icon = g_themed_icon_new ("drive-removable-media-symbolic");
244 }
245
246 info = g_new0 (GMountInfo, 1);
247 info->ref_count = 1;
248 info->display_name = g_strdup (display_name);
249 info->stable_name = g_strdup (stable_name);
250 info->x_content_types = g_strdup (x_content_types);
251 info->icon = icon;
252 info->symbolic_icon = symbolic_icon;
253 info->dbus_id = g_strdup (dbus_id);
254 info->object_path = g_strdup (obj_path);
255 info->mount_spec = mount_spec;
256 info->user_visible = user_visible;
257 info->prefered_filename_encoding = g_strdup (prefered_filename_encoding);
258 info->fuse_mountpoint = g_strdup (fuse_mountpoint);
259 info->default_location = g_strdup (default_location);
260
261 return info;
262 }
263
264 static void
g_mount_tracker_finalize(GObject * object)265 g_mount_tracker_finalize (GObject *object)
266 {
267 GMountTracker *tracker;
268
269 tracker = G_MOUNT_TRACKER (object);
270
271 g_mutex_clear (&tracker->lock);
272
273 g_list_free_full (tracker->mounts, (GDestroyNotify)g_mount_info_unref);
274
275 g_clear_object (&tracker->proxy);
276 g_clear_object (&tracker->connection);
277
278 if (G_OBJECT_CLASS (g_mount_tracker_parent_class)->finalize)
279 (*G_OBJECT_CLASS (g_mount_tracker_parent_class)->finalize) (object);
280 }
281
282 static void
g_mount_tracker_class_init(GMountTrackerClass * klass)283 g_mount_tracker_class_init (GMountTrackerClass *klass)
284 {
285 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
286
287 gobject_class->finalize = g_mount_tracker_finalize;
288 gobject_class->constructor = g_mount_tracker_constructor;
289 gobject_class->set_property = g_mount_tracker_set_property;
290 gobject_class->get_property = g_mount_tracker_get_property;
291
292 signals[MOUNTED] = g_signal_new (I_("mounted"),
293 G_TYPE_MOUNT_TRACKER,
294 G_SIGNAL_RUN_LAST,
295 G_STRUCT_OFFSET (GMountTrackerClass, mounted),
296 NULL, NULL,
297 g_cclosure_marshal_VOID__POINTER,
298 G_TYPE_NONE, 1, G_TYPE_POINTER);
299
300 signals[UNMOUNTED] = g_signal_new (I_("unmounted"),
301 G_TYPE_MOUNT_TRACKER,
302 G_SIGNAL_RUN_LAST,
303 G_STRUCT_OFFSET (GMountTrackerClass, unmounted),
304 NULL, NULL,
305 g_cclosure_marshal_VOID__POINTER,
306 G_TYPE_NONE, 1, G_TYPE_POINTER);
307
308 g_object_class_install_property (gobject_class,
309 PROP_CONNECTION,
310 g_param_spec_pointer ("connection",
311 P_("DBus connection"),
312 P_("The dbus connection to use for ipc."),
313 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
314 G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
315
316 g_object_class_install_property (gobject_class,
317 PROP_USER_VISIBLE_ONLY,
318 g_param_spec_boolean ("user-visible-only",
319 P_("User visible only"),
320 P_("User visible only"),
321 FALSE,
322 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
323 G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
324 }
325
326 static void
g_mount_tracker_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)327 g_mount_tracker_set_property (GObject *object,
328 guint prop_id,
329 const GValue *value,
330 GParamSpec *pspec)
331 {
332 GMountTracker *tracker = G_MOUNT_TRACKER (object);
333
334 switch (prop_id)
335 {
336 case PROP_CONNECTION:
337 g_clear_object (&tracker->connection);
338 if (g_value_get_pointer (value))
339 tracker->connection = g_object_ref (g_value_get_pointer (value));
340 break;
341 case PROP_USER_VISIBLE_ONLY:
342 tracker->user_visible_only = g_value_get_boolean (value);
343 break;
344 default:
345 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
346 break;
347 }
348 }
349
350 static void
g_mount_tracker_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)351 g_mount_tracker_get_property (GObject *object,
352 guint prop_id,
353 GValue *value,
354 GParamSpec *pspec)
355 {
356 GMountTracker *tracker = G_MOUNT_TRACKER (object);
357
358 switch (prop_id)
359 {
360 case PROP_CONNECTION:
361 g_value_set_pointer (value, tracker->connection);
362 break;
363 case PROP_USER_VISIBLE_ONLY:
364 g_value_set_boolean (value, tracker->user_visible_only);
365 break;
366 default:
367 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
368 break;
369 }
370 }
371
372 static GList *
g_mount_tracker_find(GMountTracker * tracker,GMountInfo * info)373 g_mount_tracker_find (GMountTracker *tracker,
374 GMountInfo *info)
375 {
376 GList *l;
377
378 for (l = tracker->mounts; l != NULL; l = l->next)
379 {
380 if (g_mount_info_equal (info, (GMountInfo *)l->data))
381 return l;
382 }
383
384 return NULL;
385 }
386
387 static void
g_mount_tracker_add_mount(GMountTracker * tracker,GMountInfo * info)388 g_mount_tracker_add_mount (GMountTracker *tracker,
389 GMountInfo *info)
390 {
391 g_mutex_lock (&tracker->lock);
392
393 /* Don't add multiple times */
394 if (g_mount_tracker_find (tracker, info))
395 {
396 g_mutex_unlock (&tracker->lock);
397 return;
398 }
399
400 if (tracker->user_visible_only && !info->user_visible)
401 {
402 g_mutex_unlock (&tracker->lock);
403 return;
404 }
405
406 tracker->mounts = g_list_prepend (tracker->mounts, g_mount_info_ref (info));
407
408 g_mutex_unlock (&tracker->lock);
409
410 g_signal_emit (tracker, signals[MOUNTED], 0, info);
411 }
412
413 static void
g_mount_tracker_remove_mount(GMountTracker * tracker,GMountInfo * info)414 g_mount_tracker_remove_mount (GMountTracker *tracker,
415 GMountInfo *info)
416 {
417 GList *l;
418 GMountInfo *old_info;
419
420 g_mutex_lock (&tracker->lock);
421
422 l = g_mount_tracker_find (tracker, info);
423
424 /* Don't remove multiple times */
425 if (l == NULL)
426 {
427 g_mutex_unlock (&tracker->lock);
428 return;
429 }
430
431 old_info = l->data;
432
433 tracker->mounts = g_list_delete_link (tracker->mounts, l);
434
435 g_mutex_unlock (&tracker->lock);
436
437 g_signal_emit (tracker, signals[UNMOUNTED], 0, old_info);
438 g_mount_info_unref (old_info);
439 }
440
441 static void
list_mounts_reply(GMountTracker * tracker,GVariant * mounts)442 list_mounts_reply (GMountTracker *tracker,
443 GVariant *mounts)
444 {
445 GMountInfo *info;
446 GVariantIter iter;
447 GVariant *child;
448
449 g_variant_iter_init (&iter, mounts);
450 while ((child = g_variant_iter_next_value (&iter)))
451 {
452 info = g_mount_info_from_dbus (child);
453 if (info)
454 {
455 g_mount_tracker_add_mount (tracker, info);
456 g_mount_info_unref (info);
457 }
458 g_variant_unref (child);
459 }
460 }
461
462 static void
mounted_cb(GVfsDBusMountTracker * object,GVariant * arg_mount,gpointer user_data)463 mounted_cb (GVfsDBusMountTracker *object,
464 GVariant *arg_mount,
465 gpointer user_data)
466 {
467 GMountTracker *tracker = G_MOUNT_TRACKER (user_data);
468 GMountInfo *info;
469
470 info = g_mount_info_from_dbus (arg_mount);
471 if (info)
472 {
473 g_mount_tracker_add_mount (tracker, info);
474 g_mount_info_unref (info);
475 }
476 }
477
478 static void
unmounted_cb(GVfsDBusMountTracker * object,GVariant * arg_mount,gpointer user_data)479 unmounted_cb (GVfsDBusMountTracker *object,
480 GVariant *arg_mount,
481 gpointer user_data)
482 {
483 GMountTracker *tracker = G_MOUNT_TRACKER (user_data);
484 GMountInfo *info;
485
486 info = g_mount_info_from_dbus (arg_mount);
487 if (info)
488 {
489 g_mount_tracker_remove_mount (tracker, info);
490 g_mount_info_unref (info);
491 }
492
493 }
494
495 /* Called after construction when the construct properties (like connection) are set */
496 static void
init_connection_sync(GMountTracker * tracker)497 init_connection_sync (GMountTracker *tracker)
498 {
499 GError *error;
500 GVariant *iter_mounts;
501 gboolean res;
502
503 if (tracker->connection == NULL)
504 tracker->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
505
506 error = NULL;
507 tracker->proxy = gvfs_dbus_mount_tracker_proxy_new_sync (tracker->connection,
508 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
509 G_VFS_DBUS_DAEMON_NAME,
510 G_VFS_DBUS_MOUNTTRACKER_PATH,
511 NULL,
512 &error);
513 if (tracker->proxy == NULL)
514 {
515 g_printerr ("Error creating proxy: %s (%s, %d)\n",
516 error->message, g_quark_to_string (error->domain), error->code);
517 g_error_free (error);
518 return;
519 }
520
521 g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (tracker->proxy),
522 G_VFS_DBUS_TIMEOUT_MSECS);
523
524 res = gvfs_dbus_mount_tracker_call_list_mounts2_sync (tracker->proxy, tracker->user_visible_only, &iter_mounts, NULL, &error);
525 if (!res)
526 {
527 if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD))
528 res = gvfs_dbus_mount_tracker_call_list_mounts_sync (tracker->proxy, &iter_mounts, NULL, NULL);
529 g_clear_error (&error);
530 }
531
532 if (res)
533 {
534 list_mounts_reply (tracker, iter_mounts);
535 g_variant_unref (iter_mounts);
536 }
537
538 g_signal_connect (tracker->proxy, "mounted", G_CALLBACK (mounted_cb), tracker);
539 g_signal_connect (tracker->proxy, "unmounted", G_CALLBACK (unmounted_cb), tracker);
540 }
541
542 static void
g_mount_tracker_init(GMountTracker * tracker)543 g_mount_tracker_init (GMountTracker *tracker)
544 {
545 g_mutex_init (&tracker->lock);
546 }
547
548
549 static GObject*
g_mount_tracker_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_params)550 g_mount_tracker_constructor (GType type,
551 guint n_construct_properties,
552 GObjectConstructParam *construct_params)
553 {
554 GObject *object;
555 GMountTracker *tracker;
556
557 object = (* G_OBJECT_CLASS (g_mount_tracker_parent_class)->constructor) (type,
558 n_construct_properties,
559 construct_params);
560
561 tracker = G_MOUNT_TRACKER (object);
562
563 init_connection_sync (tracker);
564
565 return object;
566 }
567
568 GMountTracker *
g_mount_tracker_new(GDBusConnection * connection,gboolean user_visible_only)569 g_mount_tracker_new (GDBusConnection *connection,
570 gboolean user_visible_only)
571 {
572 GMountTracker *tracker;
573
574 tracker = g_object_new (G_TYPE_MOUNT_TRACKER, "connection", connection, "user_visible_only", user_visible_only, NULL);
575
576 return tracker;
577 }
578
579 GList *
g_mount_tracker_list_mounts(GMountTracker * tracker)580 g_mount_tracker_list_mounts (GMountTracker *tracker)
581 {
582 GList *res, *l;
583 GMountInfo *copy;
584
585 g_mutex_lock (&tracker->lock);
586
587 res = NULL;
588 for (l = tracker->mounts; l != NULL; l = l->next)
589 {
590 copy = g_mount_info_ref (l->data);
591 res = g_list_prepend (res, copy);
592 }
593
594 g_mutex_unlock (&tracker->lock);
595
596 return g_list_reverse (res);
597 }
598
599 GMountInfo *
g_mount_tracker_find_by_mount_spec(GMountTracker * tracker,GMountSpec * mount_spec)600 g_mount_tracker_find_by_mount_spec (GMountTracker *tracker,
601 GMountSpec *mount_spec)
602 {
603 GList *l;
604 GMountInfo *info, *found;
605
606 g_mutex_lock (&tracker->lock);
607
608 found = NULL;
609 for (l = tracker->mounts; l != NULL; l = l->next)
610 {
611 info = l->data;
612
613 if (g_mount_spec_equal (info->mount_spec, mount_spec))
614 {
615 found = g_mount_info_ref (info);
616 break;
617 }
618 }
619
620 g_mutex_unlock (&tracker->lock);
621
622 return found;
623 }
624
625
626 gboolean
g_mount_tracker_has_mount_spec(GMountTracker * tracker,GMountSpec * mount_spec)627 g_mount_tracker_has_mount_spec (GMountTracker *tracker,
628 GMountSpec *mount_spec)
629 {
630 GList *l;
631 GMountInfo *info;
632 gboolean found;
633
634 g_mutex_lock (&tracker->lock);
635
636 found = FALSE;
637 for (l = tracker->mounts; l != NULL; l = l->next)
638 {
639 info = l->data;
640
641 if (g_mount_spec_equal (info->mount_spec, mount_spec))
642 {
643 found = TRUE;
644 break;
645 }
646 }
647
648 g_mutex_unlock (&tracker->lock);
649
650 return found;
651 }
652
653