diff options
Diffstat (limited to 'gnome-2-24/daemon/gvfsdaemon.c')
-rw-r--r-- | gnome-2-24/daemon/gvfsdaemon.c | 1060 |
1 files changed, 1060 insertions, 0 deletions
diff --git a/gnome-2-24/daemon/gvfsdaemon.c b/gnome-2-24/daemon/gvfsdaemon.c new file mode 100644 index 00000000..f9f52769 --- /dev/null +++ b/gnome-2-24/daemon/gvfsdaemon.c @@ -0,0 +1,1060 @@ +/* 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 <alexl@redhat.com> + */ + +#include <config.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/errno.h> +#include <errno.h> +#include <sys/un.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> +#include <glib-object.h> +#include <dbus-gmain.h> +#include <gvfsdaemon.h> +#include <gvfsdaemonprotocol.h> +#include <gvfsdaemonutils.h> +#include <gvfsjobmount.h> +#include <gdbusutils.h> + +enum { + PROP_0 +}; + +typedef struct { + char *obj_path; + DBusObjectPathMessageFunction callback; + gpointer data; +} RegisteredPath; + +struct _GVfsDaemon +{ + GObject parent_instance; + + GMutex *lock; + gboolean main_daemon; + + GThreadPool *thread_pool; + DBusConnection *session_bus; + GHashTable *registered_paths; + GList *jobs; + GList *job_sources; + + guint exit_tag; + + gint mount_counter; +}; + +typedef struct { + GVfsDaemon *daemon; + char *socket_dir; + guint io_watch; + DBusServer *server; + + gboolean got_dbus_connection; + gboolean got_fd_connection; + int fd; + DBusConnection *conn; +} NewConnectionData; + +static void g_vfs_daemon_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void g_vfs_daemon_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static DBusHandlerResult daemon_message_func (DBusConnection *conn, + DBusMessage *message, + gpointer data); +static DBusHandlerResult peer_to_peer_filter_func (DBusConnection *conn, + DBusMessage *message, + gpointer data); + + +G_DEFINE_TYPE (GVfsDaemon, g_vfs_daemon, G_TYPE_OBJECT) + +static void +registered_path_free (RegisteredPath *data) +{ + g_free (data->obj_path); + g_free (data); +} + +static void +g_vfs_daemon_finalize (GObject *object) +{ + GVfsDaemon *daemon; + + daemon = G_VFS_DAEMON (object); + + g_assert (daemon->jobs == NULL); + + g_hash_table_destroy (daemon->registered_paths); + g_mutex_free (daemon->lock); + + if (G_OBJECT_CLASS (g_vfs_daemon_parent_class)->finalize) + (*G_OBJECT_CLASS (g_vfs_daemon_parent_class)->finalize) (object); +} + +static void +g_vfs_daemon_class_init (GVfsDaemonClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = g_vfs_daemon_finalize; + gobject_class->set_property = g_vfs_daemon_set_property; + gobject_class->get_property = g_vfs_daemon_get_property; +} + +static void +job_handler_callback (gpointer data, + gpointer user_data) +{ + GVfsJob *job = G_VFS_JOB (data); + + g_vfs_job_run (job); +} + +static void +g_vfs_daemon_init (GVfsDaemon *daemon) +{ + gint max_threads = 1; /* TODO: handle max threads */ + DBusError error; + + daemon->lock = g_mutex_new (); + daemon->session_bus = dbus_bus_get (DBUS_BUS_SESSION, NULL); + daemon->thread_pool = g_thread_pool_new (job_handler_callback, + daemon, + max_threads, + FALSE, NULL); + /* TODO: verify thread_pool != NULL in a nicer way */ + g_assert (daemon->thread_pool != NULL); + + daemon->mount_counter = 0; + + daemon->jobs = NULL; + daemon->registered_paths = + g_hash_table_new_full (g_str_hash, g_str_equal, + NULL, (GDestroyNotify)registered_path_free); + + dbus_error_init (&error); + dbus_bus_add_match (daemon->session_bus, + "type='signal'," + "interface='org.freedesktop.DBus'," + "member='NameOwnerChanged'," + "arg0='"G_VFS_DBUS_DAEMON_NAME"'", + &error); + + if (dbus_error_is_set (&error)) + { + g_warning ("Failed to add dbus match: %s\n", error.message); + dbus_error_free (&error); + } + + if (!dbus_connection_add_filter (daemon->session_bus, + daemon_message_func, daemon, NULL)) + _g_dbus_oom (); +} + +static void +g_vfs_daemon_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ +#if 0 + GVfsDaemon *daemon; + + daemon = G_VFS_DAEMON (object); +#endif + switch (prop_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +g_vfs_daemon_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ +#if 0 + GVfsDaemon *daemon; + + daemon = G_VFS_DAEMON (object); +#endif + switch (prop_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +GVfsDaemon * +g_vfs_daemon_new (gboolean main_daemon, gboolean replace) +{ + GVfsDaemon *daemon; + DBusConnection *conn; + DBusError error; + unsigned int flags; + int ret; + + dbus_error_init (&error); + conn = dbus_bus_get (DBUS_BUS_SESSION, &error); + if (!conn) + { + g_printerr ("Failed to connect to the D-BUS daemon: %s\n", + error.message); + + dbus_error_free (&error); + return NULL; + } + + dbus_connection_setup_with_g_main (conn, NULL); + + daemon = g_object_new (G_VFS_TYPE_DAEMON, NULL); + daemon->main_daemon = main_daemon; + + /* Request name only after we've installed the message filter */ + if (main_daemon) + { + flags = DBUS_NAME_FLAG_ALLOW_REPLACEMENT | DBUS_NAME_FLAG_DO_NOT_QUEUE; + if (replace) + flags |= DBUS_NAME_FLAG_REPLACE_EXISTING; + + ret = dbus_bus_request_name (conn, G_VFS_DBUS_DAEMON_NAME, flags, &error); + if (ret == -1) + { + g_printerr ("Failed to acquire daemon name: %s", error.message); + dbus_error_free (&error); + + g_object_unref (daemon); + daemon = NULL; + } + else if (ret == DBUS_REQUEST_NAME_REPLY_EXISTS) + { + g_printerr ("VFS daemon already running, exiting.\n"); + g_object_unref (daemon); + daemon = NULL; + } + else if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) + { + g_printerr ("Not primary owner of the service, exiting.\n"); + g_object_unref (daemon); + daemon = NULL; + } + } + + dbus_connection_unref (conn); + + return daemon; +} + +void +g_vfs_daemon_set_max_threads (GVfsDaemon *daemon, + gint max_threads) +{ + g_thread_pool_set_max_threads (daemon->thread_pool, max_threads, NULL); +} + +static gboolean +exit_at_idle (gpointer data) +{ + exit (0); + return FALSE; +} + +static void +daemon_unschedule_exit (GVfsDaemon *daemon) +{ + if (daemon->exit_tag != 0) + { + g_source_remove (daemon->exit_tag); + daemon->exit_tag = 0; + } +} + +static void +daemon_schedule_exit (GVfsDaemon *daemon) +{ + if (daemon->exit_tag == 0) + daemon->exit_tag = g_timeout_add (1000, exit_at_idle, daemon); +} + +static void +job_source_new_job_callback (GVfsJobSource *job_source, + GVfsJob *job, + GVfsDaemon *daemon) +{ + g_vfs_daemon_queue_job (daemon, job); +} + +static void +job_source_closed_callback (GVfsJobSource *job_source, + GVfsDaemon *daemon) +{ + g_mutex_lock (daemon->lock); + + daemon->job_sources = g_list_remove (daemon->job_sources, + job_source); + + g_signal_handlers_disconnect_by_func (job_source, + (GCallback)job_source_new_job_callback, + daemon); + g_signal_handlers_disconnect_by_func (job_source, + (GCallback)job_source_closed_callback, + daemon); + + g_object_unref (job_source); + + if (daemon->job_sources == NULL) + daemon_schedule_exit (daemon); + + g_mutex_unlock (daemon->lock); +} + +static void +g_vfs_daemon_re_register_job_sources (GVfsDaemon *daemon) +{ + GList *l; + + g_mutex_lock (daemon->lock); + + for (l = daemon->job_sources; l != NULL; l = l->next) + { + if (G_VFS_IS_BACKEND (l->data)) + g_vfs_backend_register_mount (l->data, NULL, NULL); + } + + g_mutex_unlock (daemon->lock); +} + +void +g_vfs_daemon_add_job_source (GVfsDaemon *daemon, + GVfsJobSource *job_source) +{ + g_print ("Added new job source %p (%s)\n", job_source, g_type_name_from_instance ((gpointer)job_source)); + + g_mutex_lock (daemon->lock); + + daemon_unschedule_exit (daemon); + + g_object_ref (job_source); + daemon->job_sources = g_list_append (daemon->job_sources, + job_source); + g_signal_connect (job_source, "new_job", + (GCallback)job_source_new_job_callback, daemon); + g_signal_connect (job_source, "closed", + (GCallback)job_source_closed_callback, daemon); + + g_mutex_unlock (daemon->lock); +} + +/* This registers a dbus callback on *all* connections, client and session bus */ +void +g_vfs_daemon_register_path (GVfsDaemon *daemon, + const char *obj_path, + DBusObjectPathMessageFunction callback, + gpointer user_data) +{ + RegisteredPath *data; + + data = g_new0 (RegisteredPath, 1); + data->obj_path = g_strdup (obj_path); + data->callback = callback; + data->data = user_data; + + g_hash_table_insert (daemon->registered_paths, data->obj_path, + data); +} + +void +g_vfs_daemon_unregister_path (GVfsDaemon *daemon, + const char *obj_path) +{ + g_hash_table_remove (daemon->registered_paths, obj_path); +} + +/* NOTE: Might be emitted on a thread */ +static void +job_new_source_callback (GVfsJob *job, + GVfsJobSource *job_source, + GVfsDaemon *daemon) +{ + g_vfs_daemon_add_job_source (daemon, job_source); +} + +/* NOTE: Might be emitted on a thread */ +static void +job_finished_callback (GVfsJob *job, + GVfsDaemon *daemon) +{ + + g_signal_handlers_disconnect_by_func (job, + (GCallback)job_new_source_callback, + daemon); + g_signal_handlers_disconnect_by_func (job, + (GCallback)job_finished_callback, + daemon); + + g_mutex_lock (daemon->lock); + daemon->jobs = g_list_remove (daemon->jobs, job); + g_mutex_unlock (daemon->lock); + + g_object_unref (job); +} + +void +g_vfs_daemon_queue_job (GVfsDaemon *daemon, + GVfsJob *job) +{ + g_print ("Queued new job %p (%s)\n", job, g_type_name_from_instance ((gpointer)job)); + + g_object_ref (job); + g_signal_connect (job, "finished", (GCallback)job_finished_callback, daemon); + g_signal_connect (job, "new_source", (GCallback)job_new_source_callback, daemon); + + g_mutex_lock (daemon->lock); + daemon->jobs = g_list_prepend (daemon->jobs, job); + g_mutex_unlock (daemon->lock); + + /* Can we start the job immediately / async */ + if (!g_vfs_job_try (job)) + { + /* Couldn't finish / run async, queue worker thread */ + g_thread_pool_push (daemon->thread_pool, job, NULL); /* TODO: Check error */ + } +} + +static void +new_connection_data_free (void *memory) +{ + NewConnectionData *data = memory; + + /* Remove the socket and dir after connected */ + if (data->socket_dir) + rmdir (data->socket_dir); + + if (data->io_watch) + g_source_remove (data->io_watch); + + g_free (data->socket_dir); + g_free (data); +} + +static void +daemon_peer_connection_setup (GVfsDaemon *daemon, + DBusConnection *dbus_conn, + NewConnectionData *data) +{ + /* We wait until we have the extra fd */ + if (!data->got_fd_connection) + return; + + if (data->fd == -1) + { + /* The fd connection failed, abort the whole thing */ + g_warning ("Failed to accept client: %s", "accept of extra fd failed"); + dbus_connection_unref (dbus_conn); + goto error_out; + } + + dbus_connection_setup_with_g_main (dbus_conn, NULL); + if (!dbus_connection_add_filter (dbus_conn, peer_to_peer_filter_func, daemon, NULL) || + !dbus_connection_add_filter (dbus_conn, daemon_message_func, daemon, NULL)) + { + g_warning ("Failed to accept client: %s", "object registration failed"); + dbus_connection_unref (dbus_conn); + close (data->fd); + goto error_out; + } + + dbus_connection_add_fd_send_fd (dbus_conn, data->fd); + + error_out: + new_connection_data_free (data); +} + +#ifdef __linux__ +#define USE_ABSTRACT_SOCKETS +#endif + +static void +randomize_string (char tmp[9]) +{ + int i; + const char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + + for (i = 0; i < 8; i++) + tmp[i] = chars[g_random_int_range (0, strlen(chars))]; + + tmp[8] = '\0'; +} + +#ifndef USE_ABSTRACT_SOCKETS +static gboolean +test_safe_socket_dir (const char *dirname) +{ + struct stat statbuf; + + if (g_stat (dirname, &statbuf) != 0) + return FALSE; + +#ifndef G_PLATFORM_WIN32 + if (statbuf.st_uid != getuid ()) + return FALSE; + + if ((statbuf.st_mode & (S_IRWXG|S_IRWXO)) || + !S_ISDIR (statbuf.st_mode)) + return FALSE; +#endif + + return TRUE; +} + + +static char * +create_socket_dir (void) +{ + char *dirname; + long iteration = 0; + char *safe_dir; + gchar tmp[9]; + int i; + + safe_dir = NULL; + do + { + g_free (safe_dir); + + randomize_string (tmp); + + dirname = g_strdup_printf ("gvfs-%s-%s", + g_get_user_name (), tmp); + safe_dir = g_build_filename (g_get_tmp_dir (), dirname, NULL); + g_free (dirname); + + if (g_mkdir (safe_dir, 0700) < 0) + { + switch (errno) + { + case EACCES: + g_error ("I can't write to '%s', daemon init failed", + safe_dir); + break; + + case ENAMETOOLONG: + g_error ("Name '%s' too long your system is broken", + safe_dir); + break; + + case ENOMEM: +#ifdef ELOOP + case ELOOP: +#endif + case ENOSPC: + case ENOTDIR: + case ENOENT: + g_error ("Resource problem creating '%s'", safe_dir); + break; + + default: /* carry on going */ + break; + } + } + /* Possible race - so we re-scan. */ + + if (iteration++ == 1000) + g_error ("Cannot find a safe socket path in '%s'", g_get_tmp_dir ()); + } + while (!test_safe_socket_dir (safe_dir)); + + return safe_dir; +} +#endif + +static void +generate_addresses (char **address1, + char **address2, + char **folder) +{ + *address1 = NULL; + *address2 = NULL; + *folder = NULL; + +#ifdef USE_ABSTRACT_SOCKETS + { + gchar tmp[9]; + + randomize_string (tmp); + *address1 = g_strdup_printf ("unix:abstract=/dbus-vfs-daemon/socket-%s", tmp); + + randomize_string (tmp); + *address2 = g_strdup_printf ("unix:abstract=/dbus-vfs-daemon/socket-%s", tmp); + } +#else + { + char *dir; + + dir = create_socket_dir (); + *address1 = g_strdup_printf ("unix:path=%s/socket1", dir); + *address2 = g_strdup_printf ("unix:path=%s/socket2", dir); + *folder = dir; + } +#endif +} + +static void +daemon_new_connection_func (DBusServer *server, + DBusConnection *conn, + gpointer user_data) +{ + NewConnectionData *data; + + data = user_data; + data->got_dbus_connection = TRUE; + + /* Take ownership */ + data->conn = dbus_connection_ref (conn); + + daemon_peer_connection_setup (data->daemon, conn, data); + + /* Kill the server, no more need for it */ + dbus_server_disconnect (server); + dbus_server_unref (server); +} + +static int +unix_socket_at (const char *address) +{ + int fd; + const char *path; + size_t path_len; + struct sockaddr_un addr; + + fd = socket (PF_UNIX, SOCK_STREAM, 0); + if (fd == -1) + return -1; + +#ifdef USE_ABSTRACT_SOCKETS + path = address + strlen ("unix:abstract="); +#else + path = address + strlen ("unix:path="); +#endif + + memset (&addr, 0, sizeof (addr)); + addr.sun_family = AF_UNIX; + path_len = strlen (path); + +#ifdef USE_ABSTRACT_SOCKETS + addr.sun_path[0] = '\0'; /* this is what says "use abstract" */ + path_len++; /* Account for the extra nul byte added to the start of sun_path */ + + strncpy (&addr.sun_path[1], path, path_len); +#else /* USE_ABSTRACT_SOCKETS */ + strncpy (addr.sun_path, path, path_len); + unlink (path); +#endif /* ! USE_ABSTRACT_SOCKETS */ + + if (bind (fd, (struct sockaddr*) &addr, + G_STRUCT_OFFSET (struct sockaddr_un, sun_path) + path_len) < 0) + { + close (fd); + return -1; + } + + if (listen (fd, 30 /* backlog */) < 0) + { + close (fd); + return -1; + } + + return fd; +} + +static gboolean +accept_new_fd_client (GIOChannel *channel, + GIOCondition cond, + gpointer callback_data) +{ + NewConnectionData *data = callback_data; + int fd; + int new_fd; + struct sockaddr_un addr; + socklen_t addrlen; + + data->got_fd_connection = TRUE; + + fd = g_io_channel_unix_get_fd (channel); + + addrlen = sizeof (addr); + new_fd = accept (fd, (struct sockaddr *) &addr, &addrlen); + + data->fd = new_fd; + data->io_watch = 0; + + /* Did we already accept the dbus connection, if so, finish it now */ + if (data->got_dbus_connection) + daemon_peer_connection_setup (data->daemon, + data->conn, + data); + else if (data->fd == -1) + { + /* Didn't accept a dbus connection, and there is no need for one now */ + g_warning ("Failed to accept client: %s", "accept of extra fd failed"); + dbus_server_disconnect (data->server); + dbus_server_unref (data->server); + new_connection_data_free (data); + } + + return FALSE; +} + +static void +daemon_handle_get_connection (DBusConnection *conn, + DBusMessage *message, + GVfsDaemon *daemon) +{ + DBusServer *server; + DBusError error; + DBusMessage *reply; + gchar *address1; + gchar *address2; + NewConnectionData *data; + GIOChannel *channel; + char *socket_dir; + int fd; + + generate_addresses (&address1, &address2, &socket_dir); + + data = g_new (NewConnectionData, 1); + data->daemon = daemon; + data->socket_dir = socket_dir; + data->got_fd_connection = FALSE; + data->got_dbus_connection = FALSE; + data->fd = -1; + data->conn = NULL; + + dbus_error_init (&error); + server = dbus_server_listen (address1, &error); + if (!server) + { + reply = dbus_message_new_error_printf (message, + G_VFS_DBUS_ERROR_SOCKET_FAILED, + "Failed to create new socket: %s", + error.message); + dbus_error_free (&error); + if (reply) + { + dbus_connection_send (conn, reply, NULL); + dbus_message_unref (reply); + } + + goto error_out; + } + data->server = server; + + dbus_server_set_new_connection_function (server, + daemon_new_connection_func, + data, NULL); + dbus_server_setup_with_g_main (server, NULL); + + fd = unix_socket_at (address2); + if (fd == -1) + goto error_out; + + channel = g_io_channel_unix_new (fd); + g_io_channel_set_close_on_unref (channel, TRUE); + data->io_watch = g_io_add_watch (channel, G_IO_IN | G_IO_HUP, accept_new_fd_client, data); + g_io_channel_unref (channel); + + reply = dbus_message_new_method_return (message); + if (reply == NULL) + _g_dbus_oom (); + + if (!dbus_message_append_args (reply, + DBUS_TYPE_STRING, &address1, + DBUS_TYPE_STRING, &address2, + DBUS_TYPE_INVALID)) + _g_dbus_oom (); + + dbus_connection_send (conn, reply, NULL); + + dbus_message_unref (reply); + + g_free (address1); + g_free (address2); + + return; + + error_out: + g_free (data); + g_free (address1); + g_free (address2); + if (socket_dir) + { + rmdir (socket_dir); + g_free (socket_dir); + } +} + +static void +daemon_start_mount (GVfsDaemon *daemon, + DBusConnection *connection, + DBusMessage *message) +{ + const char *dbus_id, *obj_path; + DBusMessageIter iter; + DBusError derror; + DBusMessage *reply; + GMountSpec *mount_spec; + GMountSource *mount_source; + dbus_bool_t automount; + + dbus_message_iter_init (message, &iter); + + reply = NULL; + mount_spec = NULL; + dbus_error_init (&derror); + 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 (!_g_dbus_message_iter_get_args (&iter, &derror, + DBUS_TYPE_BOOLEAN, &automount, + 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); + } + + if (reply) + { + dbus_connection_send (connection, reply, NULL); + dbus_message_unref (reply); + } + else + { + mount_source = g_mount_source_new (dbus_id, obj_path); + g_vfs_daemon_initiate_mount (daemon, mount_spec, mount_source, automount, message); + g_object_unref (mount_source); + g_mount_spec_unref (mount_spec); + } +} + +static DBusHandlerResult +daemon_message_func (DBusConnection *conn, + DBusMessage *message, + gpointer data) +{ + GVfsDaemon *daemon = data; + RegisteredPath *registered_path; + const char *path; + char *name; + char *old_owner, *new_owner; + + path = dbus_message_get_path (message); + if (path == NULL) + path = ""; + + if (dbus_message_is_signal (message, DBUS_INTERFACE_DBUS, "NameLost")) + { + if (dbus_message_get_args (message, NULL, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID) && + strcmp (name, G_VFS_DBUS_DAEMON_NAME) == 0) + { + /* Someone else got the name (i.e. someone used --replace), exit */ + if (daemon->main_daemon) + exit (1); + } + } + else if (dbus_message_is_signal (message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) + { + if (dbus_message_get_args (message, NULL, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &old_owner, + DBUS_TYPE_STRING, &new_owner, + DBUS_TYPE_INVALID) && + strcmp (name, G_VFS_DBUS_DAEMON_NAME) == 0 && + *new_owner != 0 && + !daemon->main_daemon) + { + /* There is a new owner. Register mounts with it */ + g_vfs_daemon_re_register_job_sources (daemon); + } + + } + + + if (dbus_message_is_method_call (message, + G_VFS_DBUS_DAEMON_INTERFACE, + G_VFS_DBUS_OP_GET_CONNECTION)) + { + daemon_handle_get_connection (conn, message, daemon); + return DBUS_HANDLER_RESULT_HANDLED; + } + + if (dbus_message_is_method_call (message, + G_VFS_DBUS_DAEMON_INTERFACE, + G_VFS_DBUS_OP_CANCEL)) + { + GList *l; + dbus_uint32_t serial; + GVfsJob *job_to_cancel = NULL; + + if (dbus_message_get_args (message, NULL, + DBUS_TYPE_UINT32, &serial, + DBUS_TYPE_INVALID)) + { + g_mutex_lock (daemon->lock); + for (l = daemon->jobs; l != NULL; l = l->next) + { + GVfsJob *job = l->data; + + if (G_VFS_IS_JOB_DBUS (job) && + g_vfs_job_dbus_is_serial (G_VFS_JOB_DBUS (job), + conn, serial)) + { + job_to_cancel = g_object_ref (job); + break; + } + } + g_mutex_unlock (daemon->lock); + + + if (job_to_cancel) + { + g_vfs_job_cancel (job_to_cancel); + g_object_unref (job_to_cancel); + } + } + + return DBUS_HANDLER_RESULT_HANDLED; + } + + if (strcmp (path, G_VFS_DBUS_MOUNTABLE_PATH) == 0 && + dbus_message_is_method_call (message, + G_VFS_DBUS_MOUNTABLE_INTERFACE, + G_VFS_DBUS_MOUNTABLE_OP_MOUNT)) + { + daemon_start_mount (daemon, conn, message); + return DBUS_HANDLER_RESULT_HANDLED; + } + + registered_path = g_hash_table_lookup (daemon->registered_paths, path); + + if (registered_path) + return registered_path->callback (conn, message, registered_path->data); + else + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + + +/* Only called for peer-to-peer connections */ +static DBusHandlerResult +peer_to_peer_filter_func (DBusConnection *conn, + DBusMessage *message, + gpointer data) +{ + if (dbus_message_is_signal (message, + DBUS_INTERFACE_LOCAL, + "Disconnected")) + { + /* The peer-to-peer connection was disconnected */ + dbus_connection_unref (conn); + return DBUS_HANDLER_RESULT_HANDLED; + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +void +g_vfs_daemon_initiate_mount (GVfsDaemon *daemon, + GMountSpec *mount_spec, + GMountSource *mount_source, + gboolean is_automount, + DBusMessage *request) +{ + const char *type; + GType backend_type; + char *obj_path; + GVfsJob *job; + GVfsBackend *backend; + DBusConnection *conn; + DBusMessage *reply; + + type = g_mount_spec_get_type (mount_spec); + + backend_type = G_TYPE_INVALID; + if (type) + backend_type = g_vfs_lookup_backend (type); + + if (backend_type == G_TYPE_INVALID) + { + if (request) + { + reply = _dbus_message_new_gerror (request, + G_IO_ERROR, G_IO_ERROR_FAILED, + _("Invalid backend type")); + + /* Queues reply (threadsafely), actually sends it in mainloop */ + conn = dbus_bus_get (DBUS_BUS_SESSION, NULL); + if (conn) + { + dbus_connection_send (conn, reply, NULL); + dbus_message_unref (reply); + dbus_connection_unref (conn); + } + } + else + g_warning ("Error mounting: invalid backend type\n"); + return; + } + + obj_path = g_strdup_printf ("/org/gtk/vfs/mount/%d", ++daemon->mount_counter); + backend = g_object_new (backend_type, + "daemon", daemon, + "object_path", obj_path, + NULL); + g_free (obj_path); + + g_vfs_daemon_add_job_source (daemon, G_VFS_JOB_SOURCE (backend)); + g_object_unref (backend); + + job = g_vfs_job_mount_new (mount_spec, mount_source, is_automount, request, backend); + g_vfs_daemon_queue_job (daemon, job); +} |