/* GIO - GLib Input, Output and Streaming Library * * Copyright (C) 2006-2007 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. * * Author: Alexander Larsson */ #include #include #include #include #include #include #include #include "mount.h" #include "gmountoperationdbus.h" #include "gvfsdaemonprotocol.h" #include "gvfsdbusutils.h" #include typedef struct { char *display_name; char *stable_name; char *x_content_types; char *icon; char *prefered_filename_encoding; gboolean user_visible; char *default_location; char *fuse_mountpoint; /* Always set, even if fuse not availible */ /* Daemon object ref */ char *dbus_id; char *object_path; /* Mount details */ GMountSpec *mount_spec; } VfsMount; typedef struct { char *type; char *exec; char *dbus_name; gboolean automount; char *scheme; char **scheme_aliases; int default_port; gboolean hostname_is_inet; } VfsMountable; typedef void (*MountCallback) (VfsMountable *mountable, GError *error, gpointer user_data); static GList *mountables = NULL; static GList *mounts = NULL; static gboolean fuse_available; static void lookup_mount (DBusConnection *connection, DBusMessage *message, gboolean do_automount); static void mountable_mount (VfsMountable *mountable, GMountSpec *mount_spec, GMountSource *source, gboolean automount, MountCallback callback, gpointer user_data); static VfsMount * find_vfs_mount (const char *dbus_id, const char *obj_path) { GList *l; for (l = mounts; l != NULL; l = l->next) { VfsMount *mount = l->data; if (strcmp (mount->dbus_id, dbus_id) == 0 && strcmp (mount->object_path, obj_path) == 0) return mount; } return NULL; } static VfsMount * find_vfs_mount_by_fuse_path (const char *fuse_path) { GList *l; if (!fuse_available) return NULL; for (l = mounts; l != NULL; l = l->next) { VfsMount *mount = l->data; if (mount->fuse_mountpoint != NULL && g_str_has_prefix (fuse_path, mount->fuse_mountpoint)) { int len = strlen (mount->fuse_mountpoint); if (fuse_path[len] == 0 || fuse_path[len] == '/') return mount; } } return NULL; } static VfsMount * match_vfs_mount (GMountSpec *match) { GList *l; for (l = mounts; l != NULL; l = l->next) { VfsMount *mount = l->data; if (g_mount_spec_match (mount->mount_spec, match)) return mount; } return NULL; } static VfsMountable * find_mountable (const char *type) { GList *l; for (l = mountables; l != NULL; l = l->next) { VfsMountable *mountable = l->data; if (strcmp (mountable->type, type) == 0) return mountable; } return NULL; } static VfsMountable * lookup_mountable (GMountSpec *spec) { const char *type; type = g_mount_spec_get_type (spec); if (type == NULL) return NULL; return find_mountable (type); } static void vfs_mountable_free (VfsMountable *mountable) { g_free (mountable->type); g_free (mountable->exec); g_free (mountable->dbus_name); g_free (mountable->scheme); g_strfreev (mountable->scheme_aliases); g_free (mountable); } static void vfs_mount_free (VfsMount *mount) { g_free (mount->display_name); g_free (mount->stable_name); g_free (mount->x_content_types); g_free (mount->icon); g_free (mount->fuse_mountpoint); g_free (mount->prefered_filename_encoding); g_free (mount->default_location); g_free (mount->dbus_id); g_free (mount->object_path); g_mount_spec_unref (mount->mount_spec); g_free (mount); } static void vfs_mount_to_dbus (VfsMount *mount, DBusMessageIter *iter) { DBusMessageIter struct_iter; dbus_bool_t user_visible; char *fuse_mountpoint; if (!dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT, NULL, &struct_iter)) _g_dbus_oom (); if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &mount->dbus_id)) _g_dbus_oom (); if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_OBJECT_PATH, &mount->object_path)) _g_dbus_oom (); if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &mount->display_name)) _g_dbus_oom (); if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &mount->stable_name)) _g_dbus_oom (); if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &mount->x_content_types)) _g_dbus_oom (); if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &mount->icon)) _g_dbus_oom (); if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &mount->prefered_filename_encoding)) _g_dbus_oom (); user_visible = mount->user_visible; if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_BOOLEAN, &user_visible)) _g_dbus_oom (); fuse_mountpoint = ""; if (fuse_available && mount->fuse_mountpoint) fuse_mountpoint = mount->fuse_mountpoint; _g_dbus_message_iter_append_cstring (&struct_iter, fuse_mountpoint); g_mount_spec_to_dbus (&struct_iter, mount->mount_spec); _g_dbus_message_iter_append_cstring (&struct_iter, mount->default_location); if (!dbus_message_iter_close_container (iter, &struct_iter)) _g_dbus_oom (); } static void vfs_mountable_to_dbus (VfsMountable *mountable, DBusMessageIter *iter) { DBusMessageIter struct_iter; dbus_bool_t bool; guint32 int32; char *s; char **a; char *empty[] = {NULL}; if (!dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT, NULL, &struct_iter)) _g_dbus_oom (); if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &mountable->type)) _g_dbus_oom (); s = mountable->scheme; if (s == NULL) s = ""; if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &s)) _g_dbus_oom (); a = mountable->scheme_aliases; if (a == NULL) a = empty; _g_dbus_message_iter_append_args (&struct_iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &a, (int)g_strv_length (a), 0); int32 = mountable->default_port; if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_INT32, &int32)) _g_dbus_oom (); bool = mountable->hostname_is_inet; if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_BOOLEAN, &bool)) _g_dbus_oom (); if (!dbus_message_iter_close_container (iter, &struct_iter)) _g_dbus_oom (); } /************************************************************************ * Support for mounting a VfsMountable * ************************************************************************/ typedef struct { VfsMountable *mountable; dbus_bool_t automount; GMountSource *source; GMountSpec *mount_spec; MountCallback callback; gpointer user_data; char *obj_path; gboolean spawned; } MountData; static void spawn_mount (MountData *data); static void mount_data_free (MountData *data) { g_object_unref (data->source); g_mount_spec_unref (data->mount_spec); g_free (data->obj_path); g_free (data); } static void mount_finish (MountData *data, GError *error) { data->callback (data->mountable, error, data->user_data); mount_data_free (data); } static void dbus_mount_reply (DBusPendingCall *pending, void *_data) { DBusMessage *reply; GError *error; MountData *data = _data; reply = dbus_pending_call_steal_reply (pending); dbus_pending_call_unref (pending); if ((dbus_message_is_error (reply, DBUS_ERROR_NAME_HAS_NO_OWNER) || dbus_message_is_error (reply, DBUS_ERROR_SERVICE_UNKNOWN)) && !data->spawned) spawn_mount (data); else { error = NULL; if (_g_error_from_message (reply, &error)) { mount_finish (data, error); g_error_free (error); } else mount_finish (data, NULL); } dbus_message_unref (reply); } static void mountable_mount_with_name (MountData *data, const char *dbus_name) { DBusConnection *conn; DBusMessage *message; DBusPendingCall *pending; GError *error = NULL; DBusMessageIter iter; conn = dbus_bus_get (DBUS_BUS_SESSION, NULL); message = dbus_message_new_method_call (dbus_name, G_VFS_DBUS_MOUNTABLE_PATH, G_VFS_DBUS_MOUNTABLE_INTERFACE, G_VFS_DBUS_MOUNTABLE_OP_MOUNT); dbus_message_iter_init_append (message, &iter); g_mount_spec_to_dbus (&iter, data->mount_spec); _g_dbus_message_append_args (message, DBUS_TYPE_BOOLEAN, &data->automount, 0); g_mount_source_to_dbus (data->source, message); if (!dbus_connection_send_with_reply (conn, message, &pending, G_VFS_DBUS_MOUNT_TIMEOUT_MSECS)) _g_dbus_oom (); dbus_message_unref (message); dbus_connection_unref (conn); if (pending == NULL) { g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, "Error while getting peer-to-peer dbus connection: %s", "Connection is closed"); mount_finish (data, error); g_error_free (error); return; } if (!dbus_pending_call_set_notify (pending, dbus_mount_reply, data, NULL)) _g_dbus_oom (); } static DBusHandlerResult spawn_mount_message_function (DBusConnection *connection, DBusMessage *message, void *user_data) { MountData *data = user_data; GError *error = NULL; dbus_bool_t succeeded; char *error_message; if (dbus_message_is_method_call (message, G_VFS_DBUS_SPAWNER_INTERFACE, G_VFS_DBUS_OP_SPAWNED)) { dbus_connection_unregister_object_path (connection, data->obj_path); if (!dbus_message_get_args (message, NULL, DBUS_TYPE_BOOLEAN, &succeeded, DBUS_TYPE_STRING, &error_message, DBUS_TYPE_INVALID)) { g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Invalid arguments from spawned child")); mount_finish (data, error); g_error_free (error); } else if (!succeeded) { g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED, error_message); mount_finish (data, error); g_error_free (error); } else mountable_mount_with_name (data, dbus_message_get_sender (message)); return DBUS_HANDLER_RESULT_HANDLED; } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } static void spawn_mount (MountData *data) { char *exec; GError *error; DBusConnection *connection; static int mount_id = 0; DBusObjectPathVTable spawn_vtable = { NULL, spawn_mount_message_function }; data->spawned = TRUE; error = NULL; if (data->mountable->exec == NULL) { g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED, "No exec key defined for mountpoint"); mount_finish (data, error); g_error_free (error); } else { data->obj_path = g_strdup_printf ("/org/gtk/gvfs/exec_spaw/%d", mount_id++); connection = dbus_bus_get (DBUS_BUS_SESSION, NULL); if (!dbus_connection_register_object_path (connection, data->obj_path, &spawn_vtable, data)) _g_dbus_oom (); exec = g_strconcat (data->mountable->exec, " --spawner ", dbus_bus_get_unique_name (connection), " ", data->obj_path, NULL); if (!g_spawn_command_line_async (exec, &error)) { dbus_connection_unregister_object_path (connection, data->obj_path); mount_finish (data, error); g_error_free (error); } /* TODO: Add a timeout here to detect spawned app crashing */ dbus_connection_unref (connection); g_free (exec); } } static void mountable_mount (VfsMountable *mountable, GMountSpec *mount_spec, GMountSource *source, gboolean automount, MountCallback callback, gpointer user_data) { MountData *data; data = g_new0 (MountData, 1); data->automount = automount; data->mountable = mountable; data->source = g_object_ref (source); data->mount_spec = g_mount_spec_ref (mount_spec); data->callback = callback; data->user_data = user_data; if (mountable->dbus_name == NULL) spawn_mount (data); else mountable_mount_with_name (data, mountable->dbus_name); } static void read_mountable_config (void) { GDir *dir; char *mount_dir, *path; const char *filename; GKeyFile *keyfile; char **types; VfsMountable *mountable; int i; mount_dir = MOUNTABLE_DIR; dir = g_dir_open (mount_dir, 0, NULL); if (dir) { while ((filename = g_dir_read_name (dir)) != NULL) { path = g_build_filename (mount_dir, filename, NULL); keyfile = g_key_file_new (); if (g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, NULL)) { types = g_key_file_get_string_list (keyfile, "Mount", "Type", NULL, NULL); if (types != NULL) { for (i = 0; types[i] != NULL; i++) { if (*types[i] != 0) { mountable = g_new0 (VfsMountable, 1); mountable->type = g_strdup (types[i]); mountable->exec = g_key_file_get_string (keyfile, "Mount", "Exec", NULL); mountable->dbus_name = g_key_file_get_string (keyfile, "Mount", "DBusName", NULL); mountable->automount = g_key_file_get_boolean (keyfile, "Mount", "AutoMount", NULL); mountable->scheme = g_key_file_get_string (keyfile, "Mount", "Scheme", NULL); mountable->scheme_aliases = g_key_file_get_string_list (keyfile, "Mount", "SchemeAliases", NULL, NULL); mountable->default_port = g_key_file_get_integer (keyfile, "Mount", "DefaultPort", NULL); mountable->hostname_is_inet = g_key_file_get_boolean (keyfile, "Mount", "HostnameIsInetAddress", NULL); if (mountable->scheme == NULL) mountable->scheme = g_strdup (mountable->type); mountables = g_list_prepend (mountables, mountable); } } g_strfreev (types); } } g_key_file_free (keyfile); g_free (path); } g_dir_close (dir); } } static void re_read_mountable_config (void) { g_list_foreach (mountables, (GFunc)vfs_mountable_free, NULL); g_list_free (mountables); mountables = NULL; read_mountable_config (); } /************************************************************************ * Support for keeping track of active mounts * ************************************************************************/ static void signal_mounted_unmounted (VfsMount *mount, gboolean mounted) { DBusMessage *message; DBusMessageIter iter; DBusConnection *conn; message = dbus_message_new_signal (G_VFS_DBUS_MOUNTTRACKER_PATH, G_VFS_DBUS_MOUNTTRACKER_INTERFACE, mounted ? G_VFS_DBUS_MOUNTTRACKER_SIGNAL_MOUNTED : G_VFS_DBUS_MOUNTTRACKER_SIGNAL_UNMOUNTED ); if (message == NULL) _g_dbus_oom (); dbus_message_iter_init_append (message, &iter); vfs_mount_to_dbus (mount, &iter); conn = dbus_bus_get (DBUS_BUS_SESSION, NULL); dbus_connection_send (conn, message, NULL); dbus_connection_unref (conn); dbus_message_unref (message); } static void dbus_client_disconnected (const char *dbus_id) { GList *l, *next; next = NULL; for (l = mounts; l != NULL; l = next) { VfsMount *mount = l->data; next = l->next; if (strcmp (mount->dbus_id, dbus_id) == 0) { signal_mounted_unmounted (mount, FALSE); vfs_mount_free (mount); mounts = g_list_delete_link (mounts, l); } } } static void register_mount (DBusConnection *connection, DBusMessage *message) { VfsMount *mount; DBusMessage *reply; DBusError error; const char *display_name, *stable_name, *x_content_types, *icon, *obj_path; const char *id, *prefered_filename_encoding; char *default_location; dbus_bool_t user_visible; DBusMessageIter iter; GMountSpec *mount_spec; id = dbus_message_get_sender (message); dbus_message_iter_init (message, &iter); dbus_error_init (&error); if (_g_dbus_message_iter_get_args (&iter, &error, DBUS_TYPE_OBJECT_PATH, &obj_path, DBUS_TYPE_STRING, &display_name, DBUS_TYPE_STRING, &stable_name, DBUS_TYPE_STRING, &x_content_types, DBUS_TYPE_STRING, &icon, DBUS_TYPE_STRING, &prefered_filename_encoding, DBUS_TYPE_BOOLEAN, &user_visible, 0)) { if (find_vfs_mount (id, obj_path) != NULL) reply = dbus_message_new_error (message, DBUS_ERROR_INVALID_ARGS, "Mountpoint Already registered"); else if ((mount_spec = g_mount_spec_from_dbus (&iter)) == NULL) reply = dbus_message_new_error (message, DBUS_ERROR_INVALID_ARGS, "Error in mount spec"); else if (match_vfs_mount (mount_spec) != NULL) reply = dbus_message_new_error (message, DBUS_ERROR_INVALID_ARGS, "Mountpoint Already registered"); else { mount = g_new0 (VfsMount, 1); mount->display_name = g_strdup (display_name); mount->stable_name = g_strdup (stable_name); mount->x_content_types = g_strdup (x_content_types); mount->icon = g_strdup (icon); mount->prefered_filename_encoding = g_strdup (prefered_filename_encoding); mount->user_visible = user_visible; mount->dbus_id = g_strdup (id); mount->object_path = g_strdup (obj_path); mount->mount_spec = mount_spec; if (_g_dbus_message_iter_get_args (&iter, NULL, G_DBUS_TYPE_CSTRING, &default_location, 0)) mount->default_location = default_location; else mount->default_location = g_strdup (""); if (user_visible) { char *fs_name; /* Keep in sync with fuse daemon */ fs_name = g_uri_escape_string (mount->stable_name, "+@#$., ", TRUE); mount->fuse_mountpoint = g_build_filename (g_get_home_dir(), ".gvfs", fs_name, NULL); } mounts = g_list_prepend (mounts, mount); signal_mounted_unmounted (mount, TRUE); reply = dbus_message_new_method_return (message); } } else { reply = dbus_message_new_error (message, error.name, error.message); dbus_error_free (&error); } if (reply == NULL) _g_dbus_oom (); dbus_connection_send (connection, reply, NULL); dbus_message_unref (reply); } typedef struct { DBusMessage *message; DBusConnection *connection; } AutoMountData; static void automount_done (VfsMountable *mountable, GError *error, gpointer _data) { DBusMessage *reply; AutoMountData *data = _data; if (error) { reply = _dbus_message_new_gerror (data->message, G_IO_ERROR, G_IO_ERROR_NOT_MOUNTED, _("Automount failed: %s"), error->message); dbus_connection_send (data->connection, reply, NULL); dbus_message_unref (reply); } else lookup_mount (data->connection, data->message, FALSE); dbus_connection_unref (data->connection); dbus_message_unref (data->message); g_free (data); } static DBusMessage * maybe_automount (GMountSpec *spec, DBusMessage *message, DBusConnection *connection, gboolean do_automount) { VfsMountable *mountable; DBusMessage *reply; mountable = lookup_mountable (spec); reply = NULL; if (mountable != NULL && do_automount && mountable->automount) { AutoMountData *data; GMountSource *mount_source; g_debug ("automounting...\n"); mount_source = g_mount_source_new_dummy (); data = g_new0 (AutoMountData, 1); data->message = dbus_message_ref (message); data->connection = dbus_connection_ref (connection); mountable_mount (mountable, spec, mount_source, TRUE, automount_done, data); g_object_unref (mount_source); } else if (mountable != NULL) reply = _dbus_message_new_gerror (message, G_IO_ERROR, G_IO_ERROR_NOT_MOUNTED, _("The specified location is not mounted")); else reply = _dbus_message_new_gerror (message, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("The specified location is not supported")); return reply; } static void lookup_mount (DBusConnection *connection, DBusMessage *message, gboolean do_automount) { VfsMount *mount; DBusMessage *reply; DBusMessageIter iter; GMountSpec *spec; dbus_message_iter_init (message, &iter); spec = g_mount_spec_from_dbus (&iter); reply = NULL; if (spec != NULL) { mount = match_vfs_mount (spec); if (mount == NULL) reply = maybe_automount (spec, message, connection, do_automount); else { reply = dbus_message_new_method_return (message); if (reply) { dbus_message_iter_init_append (reply, &iter); vfs_mount_to_dbus (mount, &iter); } } } else reply = dbus_message_new_error (message, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"); g_mount_spec_unref (spec); if (reply != NULL) { dbus_connection_send (connection, reply, NULL); dbus_message_unref (reply); } } static void lookup_mount_by_fuse_path (DBusConnection *connection, DBusMessage *message) { VfsMount *mount; DBusMessage *reply; DBusMessageIter iter; char *fuse_path; dbus_message_iter_init (message, &iter); reply = NULL; if (_g_dbus_message_iter_get_args (&iter, NULL, G_DBUS_TYPE_CSTRING, &fuse_path, 0)) { mount = find_vfs_mount_by_fuse_path (fuse_path); if (mount == NULL) reply = _dbus_message_new_gerror (message, G_IO_ERROR, G_IO_ERROR_NOT_MOUNTED, _("The specified location is not mounted")); else { reply = dbus_message_new_method_return (message); if (reply) { dbus_message_iter_init_append (reply, &iter); vfs_mount_to_dbus (mount, &iter); } } } else reply = dbus_message_new_error (message, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"); if (reply != NULL) { dbus_connection_send (connection, reply, NULL); dbus_message_unref (reply); } } static void list_mounts (DBusConnection *connection, DBusMessage *message) { VfsMount *mount; DBusMessage *reply; DBusMessageIter iter, array_iter; GList *l; reply = dbus_message_new_method_return (message); if (reply == NULL) _g_dbus_oom (); dbus_message_iter_init_append (reply, &iter); if (!dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, DBUS_STRUCT_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_BOOLEAN_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING G_MOUNT_SPEC_TYPE_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING DBUS_STRUCT_END_CHAR_AS_STRING, &array_iter)) _g_dbus_oom (); for (l = mounts; l != NULL; l = l->next) { mount = l->data; vfs_mount_to_dbus (mount, &array_iter); } if (!dbus_message_iter_close_container (&iter, &array_iter)) _g_dbus_oom (); dbus_connection_send (connection, reply, NULL); dbus_message_unref (reply); } static void mount_location_done (VfsMountable *mountable, GError *error, gpointer user_data) { DBusMessage *message, *reply; DBusConnection *conn; message = user_data; if (error) reply = _dbus_message_new_from_gerror (message, error); else reply = dbus_message_new_method_return (message); dbus_message_unref (message); if (reply == NULL) _g_dbus_oom (); conn = dbus_bus_get (DBUS_BUS_SESSION, NULL); if (conn) { dbus_connection_send (conn, reply, NULL); dbus_connection_unref (conn); } dbus_message_unref (reply); } static void mount_location (DBusConnection *connection, DBusMessage *message) { DBusMessageIter iter; DBusMessage *reply; DBusError derror; GMountSpec *spec; const char *obj_path, *dbus_id; VfsMountable *mountable; dbus_message_iter_init (message, &iter); mountable = NULL; spec = NULL; reply = NULL; spec = g_mount_spec_from_dbus (&iter); if (spec == NULL) reply = dbus_message_new_error (message, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"); else { dbus_error_init (&derror); if (!_g_dbus_message_iter_get_args (&iter, &derror, DBUS_TYPE_STRING, &dbus_id, DBUS_TYPE_OBJECT_PATH, &obj_path, 0)) { reply = dbus_message_new_error (message, derror.name, derror.message); dbus_error_free (&derror); } else { VfsMount *mount; mount = match_vfs_mount (spec); if (mount != NULL) reply = _dbus_message_new_gerror (message, G_IO_ERROR, G_IO_ERROR_ALREADY_MOUNTED, _("Location is already mounted")); else { mountable = lookup_mountable (spec); if (mountable == NULL) reply = _dbus_message_new_gerror (message, G_IO_ERROR, G_IO_ERROR_NOT_MOUNTED, _("Location is not mountable")); } } } if (reply) { dbus_connection_send (connection, reply, NULL); dbus_message_unref (reply); } else { GMountSource *source; source = g_mount_source_new (dbus_id, obj_path); mountable_mount (mountable, spec, source, FALSE, mount_location_done, dbus_message_ref (message)); g_object_unref (source); } if (spec) g_mount_spec_unref (spec); } static void list_mount_types (DBusConnection *connection, DBusMessage *message) { VfsMountable *mountable; DBusMessage *reply; DBusMessageIter iter, array_iter; GList *l; reply = dbus_message_new_method_return (message); if (reply == NULL) _g_dbus_oom (); dbus_message_iter_init_append (reply, &iter); if (!dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &array_iter)) _g_dbus_oom (); for (l = mountables; l != NULL; l = l->next) { mountable = l->data; if (!dbus_message_iter_append_basic (&array_iter, DBUS_TYPE_STRING, &mountable->type)) _g_dbus_oom (); } if (!dbus_message_iter_close_container (&iter, &array_iter)) _g_dbus_oom (); dbus_connection_send (connection, reply, NULL); dbus_message_unref (reply); } static void list_mountable_info (DBusConnection *connection, DBusMessage *message) { VfsMountable *mountable; DBusMessage *reply; DBusMessageIter iter, array_iter; GList *l; reply = dbus_message_new_method_return (message); if (reply == NULL) _g_dbus_oom (); dbus_message_iter_init_append (reply, &iter); if (!dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, DBUS_STRUCT_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING /* type */ DBUS_TYPE_STRING_AS_STRING /* scheme */ DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING /* scheme aliases */ DBUS_TYPE_INT32_AS_STRING /* default port */ DBUS_TYPE_BOOLEAN_AS_STRING /* host is inet */ DBUS_STRUCT_END_CHAR_AS_STRING, &array_iter)) _g_dbus_oom (); for (l = mountables; l != NULL; l = l->next) { mountable = l->data; vfs_mountable_to_dbus (mountable, &array_iter); } if (!dbus_message_iter_close_container (&iter, &array_iter)) _g_dbus_oom (); dbus_connection_send (connection, reply, NULL); dbus_message_unref (reply); } static DBusHandlerResult dbus_message_function (DBusConnection *connection, DBusMessage *message, void *user_data) { DBusHandlerResult res; res = DBUS_HANDLER_RESULT_HANDLED; if (dbus_message_is_method_call (message, G_VFS_DBUS_MOUNTTRACKER_INTERFACE, G_VFS_DBUS_MOUNTTRACKER_OP_REGISTER_FUSE)) fuse_available = TRUE; else if (dbus_message_is_method_call (message, G_VFS_DBUS_MOUNTTRACKER_INTERFACE, G_VFS_DBUS_MOUNTTRACKER_OP_REGISTER_MOUNT)) register_mount (connection, message); else if (dbus_message_is_method_call (message, G_VFS_DBUS_MOUNTTRACKER_INTERFACE, G_VFS_DBUS_MOUNTTRACKER_OP_LOOKUP_MOUNT)) lookup_mount (connection, message, TRUE); else if (dbus_message_is_method_call (message, G_VFS_DBUS_MOUNTTRACKER_INTERFACE, G_VFS_DBUS_MOUNTTRACKER_OP_LOOKUP_MOUNT_BY_FUSE_PATH)) lookup_mount_by_fuse_path (connection, message); else if (dbus_message_is_method_call (message, G_VFS_DBUS_MOUNTTRACKER_INTERFACE, G_VFS_DBUS_MOUNTTRACKER_OP_LIST_MOUNTS)) list_mounts (connection, message); else if (dbus_message_is_method_call (message, G_VFS_DBUS_MOUNTTRACKER_INTERFACE, G_VFS_DBUS_MOUNTTRACKER_OP_MOUNT_LOCATION)) mount_location (connection, message); else if (dbus_message_is_method_call (message, G_VFS_DBUS_MOUNTTRACKER_INTERFACE, G_VFS_DBUS_MOUNTTRACKER_OP_LIST_MOUNT_TYPES)) list_mount_types (connection, message); else if (dbus_message_is_method_call (message, G_VFS_DBUS_MOUNTTRACKER_INTERFACE, G_VFS_DBUS_MOUNTTRACKER_OP_LIST_MOUNTABLE_INFO)) list_mountable_info (connection, message); else res = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; return res; } static struct DBusObjectPathVTable tracker_dbus_vtable = { NULL, dbus_message_function }; static DBusHandlerResult mount_tracker_filter_func (DBusConnection *conn, DBusMessage *message, gpointer data) { const char *name, *from, *to; if (dbus_message_is_signal (message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) { if (dbus_message_get_args (message, NULL, DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &from, DBUS_TYPE_STRING, &to, DBUS_TYPE_INVALID)) { if (*name == ':' && *to == 0) dbus_client_disconnected (name); } } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } static int reload_pipes[2]; static void sigusr1_handler (int sig) { while (write (reload_pipes[1], "a", 1) != 1) ; } static gboolean reload_pipes_cb (GIOChannel *io, GIOCondition condition, gpointer data) { char a; while (read (reload_pipes[0], &a, 1) != 1) ; re_read_mountable_config (); return TRUE; } void mount_init (void) { DBusConnection *conn; DBusError error; struct sigaction sa; GIOChannel *io; read_mountable_config (); if (pipe (reload_pipes) != -1) { io = g_io_channel_unix_new (reload_pipes[0]); g_io_add_watch (io, G_IO_IN, reload_pipes_cb, NULL); sa.sa_handler = sigusr1_handler; sigemptyset (&sa.sa_mask); sa.sa_flags = 0; sigaction (SIGUSR1, &sa, NULL); } conn = dbus_bus_get (DBUS_BUS_SESSION, NULL); if (!dbus_connection_register_object_path (conn, G_VFS_DBUS_MOUNTTRACKER_PATH, &tracker_dbus_vtable, NULL)) _g_dbus_oom (); if (!dbus_connection_add_filter (conn, mount_tracker_filter_func, NULL, NULL)) _g_dbus_oom (); dbus_error_init (&error); dbus_bus_add_match (conn, "sender='org.freedesktop.DBus'," "interface='org.freedesktop.DBus'," "member='NameOwnerChanged'", &error); if (dbus_error_is_set (&error)) { g_warning ("Failed to add dbus match: %s\n", error.message); dbus_error_free (&error); } }