summaryrefslogtreecommitdiff
path: root/gdbus/gdbusserver.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdbus/gdbusserver.c')
-rw-r--r--gdbus/gdbusserver.c890
1 files changed, 890 insertions, 0 deletions
diff --git a/gdbus/gdbusserver.c b/gdbus/gdbusserver.c
new file mode 100644
index 0000000..5acd46b
--- /dev/null
+++ b/gdbus/gdbusserver.c
@@ -0,0 +1,890 @@
+/* GDBus - GLib D-Bus Library
+ *
+ * Copyright (C) 2008-2009 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: David Zeuthen <davidz@redhat.com>
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <glib/gi18n.h>
+#include <unistd.h>
+#include <errno.h>
+
+#ifdef G_OS_UNIX
+#include <gio/gunixsocketaddress.h>
+#endif
+
+#include "gdbustypes.h"
+#include "gdbusaddress.h"
+#include "gdbusutils.h"
+#include "gdbusconnection.h"
+#include "gdbusserver.h"
+#include "gdbusenumtypes.h"
+#include "gdbus-marshal.h"
+#include "gdbusprivate.h"
+
+/**
+ * SECTION:gdbusserver
+ * @short_description: Helper for accepting connections
+ * @include: gdbus/gdbus.h
+ *
+ * #GDBusServer is a helper for listening to and accepting D-Bus
+ * connections.
+ */
+
+struct _GDBusServerPrivate
+{
+ GDBusServerFlags flags;
+ gchar *address;
+ gchar *guid;
+
+ guchar *nonce;
+ gchar *nonce_file;
+
+ gchar *client_address;
+
+ GSocketListener *listener;
+ gboolean is_using_listener;
+
+ /* The result of g_main_context_get_thread_default() when the object
+ * was created (the GObject _init() function) - this is used for delivery
+ * of the :new-connection GObject signal.
+ */
+ GMainContext *main_context_at_construction;
+};
+
+enum
+{
+ PROP_0,
+ PROP_ADDRESS,
+ PROP_CLIENT_ADDRESS,
+ PROP_FLAGS,
+ PROP_GUID,
+};
+
+enum
+{
+ NEW_CONNECTION_SIGNAL,
+ LAST_SIGNAL,
+};
+
+guint _signals[LAST_SIGNAL] = {0};
+
+static void initable_iface_init (GInitableIface *initable_iface);
+
+G_DEFINE_TYPE_WITH_CODE (GDBusServer, g_dbus_server, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)
+ );
+
+static void
+g_dbus_server_finalize (GObject *object)
+{
+ GDBusServer *server = G_DBUS_SERVER (object);
+
+ if (server->priv->listener != NULL)
+ g_object_unref (server->priv->listener);
+
+ g_free (server->priv->address);
+ g_free (server->priv->guid);
+ g_free (server->priv->client_address);
+ if (server->priv->nonce != NULL)
+ {
+ memset (server->priv->nonce, '\0', 16);
+ g_free (server->priv->nonce);
+ }
+ /* we could unlink the nonce file but I don't
+ * think it's really worth the effort/risk
+ */
+ g_free (server->priv->nonce_file);
+
+ if (server->priv->main_context_at_construction != NULL)
+ g_main_context_unref (server->priv->main_context_at_construction);
+
+ if (G_OBJECT_CLASS (g_dbus_server_parent_class)->finalize != NULL)
+ G_OBJECT_CLASS (g_dbus_server_parent_class)->finalize (object);
+}
+
+static void
+g_dbus_server_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GDBusServer *server = G_DBUS_SERVER (object);
+
+ switch (prop_id)
+ {
+ case PROP_FLAGS:
+ g_value_set_flags (value, server->priv->flags);
+ break;
+
+ case PROP_GUID:
+ g_value_set_string (value, server->priv->guid);
+ break;
+
+ case PROP_ADDRESS:
+ g_value_set_string (value, server->priv->address);
+ break;
+
+ case PROP_CLIENT_ADDRESS:
+ g_value_set_string (value, server->priv->client_address);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+g_dbus_server_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GDBusServer *server = G_DBUS_SERVER (object);
+
+ switch (prop_id)
+ {
+ case PROP_FLAGS:
+ server->priv->flags = g_value_get_flags (value);
+ break;
+
+ case PROP_GUID:
+ server->priv->guid = g_value_dup_string (value);
+ break;
+
+ case PROP_ADDRESS:
+ server->priv->address = g_value_dup_string (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+g_dbus_server_class_init (GDBusServerClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->finalize = g_dbus_server_finalize;
+ gobject_class->set_property = g_dbus_server_set_property;
+ gobject_class->get_property = g_dbus_server_get_property;
+
+ /**
+ * GDBusServer:flags:
+ *
+ * Flags from the #GDBusServerFlags enumeration.
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_FLAGS,
+ g_param_spec_flags ("flags",
+ _("Flags"),
+ _("Flags for the server"),
+ G_TYPE_DBUS_SERVER_FLAGS,
+ G_DBUS_SERVER_FLAGS_NONE,
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB |
+ G_PARAM_STATIC_NICK));
+
+ /**
+ * GDBusServer:guid:
+ *
+ * The guid of the server.
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_GUID,
+ g_param_spec_string ("guid",
+ _("GUID"),
+ _("The guid of the server"),
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB |
+ G_PARAM_STATIC_NICK));
+
+ /**
+ * GDBusServer:address:
+ *
+ * The D-Bus address to listen on.
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_ADDRESS,
+ g_param_spec_string ("address",
+ _("Address"),
+ _("The address to listen on"),
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB |
+ G_PARAM_STATIC_NICK));
+
+ /**
+ * GDBusServer:client-address:
+ *
+ * The D-Bus address that clients can use.
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_CLIENT_ADDRESS,
+ g_param_spec_string ("client-address",
+ _("Client Address"),
+ _("The address clients can use"),
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB |
+ G_PARAM_STATIC_NICK));
+
+ /**
+ * GDBusServer::new-connection:
+ * @server: The #GDBusServer emitting the signal.
+ * @connection: A #GDBusConnection for the new connection.
+ *
+ * Emitted when a new authenticated connection has been made.
+ *
+ * If you want to accept the connection, simply ref the @connection
+ * object and call g_dbus_connection_close() + unref it when you are
+ * done with it.
+ *
+ * If #GDBusServer:flags contains %G_DBUS_SERVER_FLAGS_RUN_IN_THREAD
+ * then the signal is emitted in a new thread dedicated to the
+ * connection. Otherwise the signal is emitted in the <link
+ * linkend="g-main-context-push-thread-default">thread-default main
+ * loop</link> of the thread where @server was constructed in.
+ */
+ _signals[NEW_CONNECTION_SIGNAL] = g_signal_new ("new-connection",
+ G_TYPE_DBUS_SERVER,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GDBusServerClass, new_connection),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_DBUS_CONNECTION);
+
+
+ g_type_class_add_private (klass, sizeof (GDBusServerPrivate));
+}
+
+static void
+g_dbus_server_init (GDBusServer *server)
+{
+ server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, G_TYPE_DBUS_SERVER, GDBusServerPrivate);
+
+ server->priv->main_context_at_construction = g_main_context_get_thread_default ();
+ if (server->priv->main_context_at_construction != NULL)
+ g_main_context_ref (server->priv->main_context_at_construction);
+}
+
+static gboolean
+on_run (GSocketService *service,
+ GSocketConnection *socket_connection,
+ GObject *source_object,
+ gpointer user_data);
+
+/**
+ * g_dbus_server_new_sync:
+ * @address: A D-Bus address.
+ * @flags: Flags from the #GDBusServerFlags enumeration.
+ * @guid: A D-Bus GUID.
+ * @cancellable: A #GCancellable or %NULL.
+ * @error: Return location for server or %NULL.
+ *
+ * Creates a new D-Bus server that listens on the first address in
+ * @address that works.
+ *
+ * Once constructed, you can use g_dbus_server_get_client_address() to
+ * get a D-Bus address string that clients can use to connect.
+ *
+ * Connect to the #GDBusServer::new-connection signal to handle
+ * incoming connections.
+ *
+ * This is a synchronous failable constructor. See
+ * g_dbus_server_new() for the asynchronous version.
+ *
+ * Returns: A #GDBusServer or %NULL if @error is set. Free with
+ * g_object_unref().
+ */
+GDBusServer *
+g_dbus_server_new_sync (const gchar *address,
+ GDBusServerFlags flags,
+ const gchar *guid,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GDBusServer *server;
+
+ g_return_val_if_fail (address != NULL, NULL);
+ g_return_val_if_fail (g_dbus_is_guid (guid), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ server = g_initable_new (G_TYPE_DBUS_SERVER,
+ cancellable,
+ error,
+ "address", address,
+ "flags", flags,
+ "guid", guid,
+ NULL);
+ if (server != NULL)
+ {
+ /* Right now we don't have any transport not using the listener... */
+ g_assert (server->priv->is_using_listener);
+ g_socket_service_start (G_SOCKET_SERVICE (server->priv->listener));
+ g_signal_connect (G_SOCKET_SERVICE (server->priv->listener),
+ "run",
+ G_CALLBACK (on_run),
+ server);
+ }
+
+ return server;
+}
+
+/**
+ * g_dbus_server_get_client_address:
+ * @server: A #GDBusServer.
+ *
+ * Gets a D-Bus address string that can be used by clients to connect
+ * to @server.
+ *
+ * Returns: A D-Bus address string. Do not free, the string is owned
+ * by @server.
+ */
+const gchar *
+g_dbus_server_get_client_address (GDBusServer *server)
+{
+ g_return_val_if_fail (G_IS_DBUS_SERVER (server), NULL);
+ return server->priv->client_address;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+#ifdef G_OS_UNIX
+
+static gint
+random_ascii (void)
+{
+ gint ret;
+ ret = g_random_int_range (0, 60);
+ if (ret < 25)
+ ret += 'A';
+ else if (ret < 50)
+ ret += 'a' - 25;
+ else
+ ret += '0' - 50;
+ return ret;
+}
+
+/* note that address_entry has already been validated => exactly one of path, tmpdir or abstract keys are set */
+static gboolean
+try_unix (GDBusServer *server,
+ const gchar *address_entry,
+ GHashTable *key_value_pairs,
+ GError **error)
+{
+ gboolean ret;
+ const gchar *path;
+ const gchar *tmpdir;
+ const gchar *abstract;
+ GSocketAddress *address;
+
+ ret = FALSE;
+ address = NULL;
+
+ path = g_hash_table_lookup (key_value_pairs, "path");
+ tmpdir = g_hash_table_lookup (key_value_pairs, "tmpdir");
+ abstract = g_hash_table_lookup (key_value_pairs, "abstract");
+
+ if (path != NULL)
+ {
+ address = g_unix_socket_address_new (path);
+ }
+ else if (tmpdir != NULL)
+ {
+ gint n;
+ GString *s;
+ GError *local_error;
+
+ retry:
+ s = g_string_new (tmpdir);
+ g_string_append (s, "/dbus-");
+ for (n = 0; n < 8; n++)
+ g_string_append_c (s, random_ascii ());
+
+ /* prefer abstract namespace if available */
+ if (g_unix_socket_address_abstract_names_supported ())
+ address = g_unix_socket_address_new_with_type (s->str,
+ -1,
+ G_UNIX_SOCKET_ADDRESS_ABSTRACT);
+ else
+ address = g_unix_socket_address_new (s->str);
+ g_string_free (s, TRUE);
+
+ local_error = NULL;
+ if (!g_socket_listener_add_address (server->priv->listener,
+ address,
+ G_SOCKET_TYPE_STREAM,
+ G_SOCKET_PROTOCOL_DEFAULT,
+ NULL, /* source_object */
+ NULL, /* effective_address */
+ &local_error))
+ {
+ if (local_error->domain == G_IO_ERROR && local_error->code == G_IO_ERROR_ADDRESS_IN_USE)
+ {
+ g_error_free (local_error);
+ goto retry;
+ }
+ g_propagate_error (error, local_error);
+ goto out;
+ }
+ ret = TRUE;
+ goto out;
+ }
+ else if (abstract != NULL)
+ {
+ if (!g_unix_socket_address_abstract_names_supported ())
+ {
+ g_set_error_literal (error,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ _("Abstract name space not supported"));
+ goto out;
+ }
+ address = g_unix_socket_address_new_with_type (abstract,
+ -1,
+ G_UNIX_SOCKET_ADDRESS_ABSTRACT);
+ }
+ else
+ {
+ g_assert_not_reached ();
+ }
+
+ if (!g_socket_listener_add_address (server->priv->listener,
+ address,
+ G_SOCKET_TYPE_STREAM,
+ G_SOCKET_PROTOCOL_DEFAULT,
+ NULL, /* source_object */
+ NULL, /* effective_address */
+ error))
+ goto out;
+
+ ret = TRUE;
+
+ out:
+
+ if (address != NULL)
+ {
+ /* Fill out client_address if the connection attempt worked */
+ if (ret)
+ {
+ server->priv->is_using_listener = TRUE;
+
+ switch (g_unix_socket_address_get_address_type (G_UNIX_SOCKET_ADDRESS (address)))
+ {
+ case G_UNIX_SOCKET_ADDRESS_ABSTRACT:
+ server->priv->client_address = g_strdup_printf ("unix:abstract=%s",
+ g_unix_socket_address_get_path (G_UNIX_SOCKET_ADDRESS (address)));
+ break;
+
+ case G_UNIX_SOCKET_ADDRESS_PATH:
+ server->priv->client_address = g_strdup_printf ("unix:path=%s",
+ g_unix_socket_address_get_path (G_UNIX_SOCKET_ADDRESS (address)));
+ break;
+
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ }
+ g_object_unref (address);
+ }
+ return ret;
+}
+#endif
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/* note that address_entry has already been validated =>
+ * both host and port (guranteed to be a number in [0, 65535]) are set (family is optional)
+ */
+static gboolean
+try_tcp (GDBusServer *server,
+ const gchar *address_entry,
+ GHashTable *key_value_pairs,
+ gboolean do_nonce,
+ GError **error)
+{
+ gboolean ret;
+ const gchar *host;
+ const gchar *port;
+ const gchar *family;
+ gint port_num;
+ GSocketAddress *address;
+ GResolver *resolver;
+ GList *resolved_addresses;
+ GList *l;
+
+ ret = FALSE;
+ address = NULL;
+ resolver = NULL;
+ resolved_addresses = NULL;
+
+ host = g_hash_table_lookup (key_value_pairs, "host");
+ port = g_hash_table_lookup (key_value_pairs, "port");
+ family = g_hash_table_lookup (key_value_pairs, "family");
+ if (g_hash_table_lookup (key_value_pairs, "noncefile") != NULL)
+ {
+ g_set_error_literal (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ _("Cannot specify nonce file when creating a server"));
+ goto out;
+ }
+
+ if (host == NULL)
+ host = "localhost";
+ if (port == NULL)
+ port = "0";
+ port_num = strtol (port, NULL, 10);
+
+ resolver = g_resolver_get_default ();
+ resolved_addresses = g_resolver_lookup_by_name (resolver,
+ host,
+ NULL,
+ error);
+ if (resolved_addresses == NULL)
+ {
+ goto out;
+ }
+ /* TODO: handle family */
+ for (l = resolved_addresses; l != NULL; l = l->next)
+ {
+ GInetAddress *address = G_INET_ADDRESS (l->data);
+ GSocketAddress *socket_address;
+ GSocketAddress *effective_address;
+
+ socket_address = g_inet_socket_address_new (address, port_num);
+ if (!g_socket_listener_add_address (server->priv->listener,
+ socket_address,
+ G_SOCKET_TYPE_STREAM,
+ G_SOCKET_PROTOCOL_TCP,
+ NULL, /* GObject *source_object */
+ &effective_address,
+ error))
+ {
+ g_object_unref (socket_address);
+ goto out;
+ }
+ if (port_num == 0)
+ {
+ /* make sure we allocate the same port number for other listeners */
+ port_num = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (effective_address));
+ }
+ g_object_unref (effective_address);
+ g_object_unref (socket_address);
+ }
+
+ if (do_nonce)
+ {
+ gint fd;
+ guint n;
+ gsize bytes_written;
+ gsize bytes_remaining;
+
+ server->priv->nonce = g_new0 (guchar, 16);
+ for (n = 0; n < 16; n++)
+ server->priv->nonce[n] = g_random_int_range (0, 256);
+ fd = g_file_open_tmp ("gdbus-nonce-file-XXXXXX",
+ &server->priv->nonce_file,
+ error);
+ if (fd == -1)
+ {
+ g_socket_listener_close (server->priv->listener);
+ goto out;
+ }
+ again:
+ bytes_written = 0;
+ bytes_remaining = 16;
+ while (bytes_remaining > 0)
+ {
+ gssize ret;
+ ret = write (fd, server->priv->nonce + bytes_written, bytes_remaining);
+ if (ret == -1)
+ {
+ if (errno == EINTR)
+ goto again;
+ g_set_error (error,
+ G_IO_ERROR,
+ g_io_error_from_errno (errno),
+ _("Error writing nonce file at `%s': %s"),
+ server->priv->nonce_file,
+ strerror (errno));
+ goto out;
+ }
+ bytes_written += ret;
+ bytes_remaining -= ret;
+ }
+ close (fd);
+ server->priv->client_address = g_strdup_printf ("nonce-tcp:host=%s,port=%d,noncefile=%s",
+ host,
+ port_num,
+ server->priv->nonce_file);
+ }
+ else
+ {
+ server->priv->client_address = g_strdup_printf ("tcp:host=%s,port=%d", host, port_num);
+ }
+ server->priv->is_using_listener = TRUE;
+ ret = TRUE;
+
+ out:
+ g_list_foreach (resolved_addresses, (GFunc) g_object_unref, NULL);
+ g_list_free (resolved_addresses);
+ g_object_unref (resolver);
+ return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct
+{
+ GDBusServer *server;
+ GDBusConnection *connection;
+} EmitIdleData;
+
+static void
+emit_idle_data_free (EmitIdleData *data)
+{
+ g_object_unref (data->server);
+ g_object_unref (data->connection);
+ g_free (data);
+}
+
+static gboolean
+emit_new_connection_in_idle (gpointer user_data)
+{
+ EmitIdleData *data = user_data;
+
+ g_signal_emit (data->server,
+ _signals[NEW_CONNECTION_SIGNAL],
+ 0,
+ data->connection);
+ g_object_unref (data->connection);
+
+ return FALSE;
+}
+
+/* Called in new thread */
+static gboolean
+on_run (GSocketService *service,
+ GSocketConnection *socket_connection,
+ GObject *source_object,
+ gpointer user_data)
+{
+ GDBusServer *server = G_DBUS_SERVER (user_data);
+ GDBusConnection *connection;
+ GDBusConnectionFlags connection_flags;
+
+ if (server->priv->nonce != NULL)
+ {
+ gchar buf[16];
+ gsize bytes_read;
+
+ if (!g_input_stream_read_all (g_io_stream_get_input_stream (G_IO_STREAM (socket_connection)),
+ buf,
+ 16,
+ &bytes_read,
+ NULL, /* GCancellable */
+ NULL)) /* GError */
+ goto out;
+
+ if (bytes_read != 16)
+ goto out;
+
+ if (memcmp (buf, server->priv->nonce, 16) != 0)
+ goto out;
+ }
+
+ connection_flags = G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER;
+ if (server->priv->flags & G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS)
+ connection_flags |= G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS;
+
+ connection = g_dbus_connection_new_sync (G_IO_STREAM (socket_connection),
+ server->priv->guid,
+ connection_flags,
+ NULL, /* GCancellable */
+ NULL); /* GError */
+ if (connection == NULL)
+ goto out;
+
+ if (server->priv->flags & G_DBUS_SERVER_FLAGS_RUN_IN_THREAD)
+ {
+ g_signal_emit (server,
+ _signals[NEW_CONNECTION_SIGNAL],
+ 0,
+ connection);
+ g_object_unref (connection);
+ }
+ else
+ {
+ GSource *idle_source;
+ EmitIdleData *data;
+
+ data = g_new0 (EmitIdleData, 1);
+ data->server = g_object_ref (server);
+ data->connection = g_object_ref (connection);
+
+ idle_source = g_idle_source_new ();
+ g_source_set_priority (idle_source, G_PRIORITY_DEFAULT);
+ g_source_set_callback (idle_source,
+ emit_new_connection_in_idle,
+ data,
+ (GDestroyNotify) emit_idle_data_free);
+ g_source_attach (idle_source, server->priv->main_context_at_construction);
+ g_source_unref (idle_source);
+ }
+
+ out:
+ return TRUE;
+}
+
+static gboolean
+initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GDBusServer *server = G_DBUS_SERVER (initable);
+ gboolean ret;
+ guint n;
+ gchar **addr_array;
+ GError *last_error;
+
+ ret = FALSE;
+ last_error = NULL;
+
+ if (!g_dbus_is_guid (server->priv->guid))
+ {
+ g_set_error (&last_error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ _("The string `%s' is not a valid D-Bus GUID"),
+ server->priv->guid);
+ goto out;
+ }
+
+ server->priv->listener = G_SOCKET_LISTENER (g_threaded_socket_service_new (-1));
+
+ addr_array = g_strsplit (server->priv->address, ";", 0);
+ last_error = NULL;
+ for (n = 0; addr_array != NULL && addr_array[n] != NULL; n++)
+ {
+ const gchar *address_entry = addr_array[n];
+ GHashTable *key_value_pairs;
+ gchar *transport_name;
+ GError *this_error;
+
+ this_error = NULL;
+ if (g_dbus_is_supported_address (address_entry,
+ &this_error) &&
+ _g_dbus_address_parse_entry (address_entry,
+ &transport_name,
+ &key_value_pairs,
+ &this_error))
+ {
+
+ if (FALSE)
+ {
+ }
+#ifdef G_OS_UNIX
+ else if (g_strcmp0 (transport_name, "unix") == 0)
+ {
+ ret = try_unix (server, address_entry, key_value_pairs, &this_error);
+ }
+#endif
+ else if (g_strcmp0 (transport_name, "tcp") == 0)
+ {
+ ret = try_tcp (server, address_entry, key_value_pairs, FALSE, &this_error);
+ }
+ else if (g_strcmp0 (transport_name, "nonce-tcp") == 0)
+ {
+ ret = try_tcp (server, address_entry, key_value_pairs, TRUE, &this_error);
+ }
+ else
+ {
+ g_set_error (&this_error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ _("Cannot listen on unsupported transport `%s'"),
+ transport_name);
+ }
+
+ g_free (transport_name);
+ if (key_value_pairs != NULL)
+ g_hash_table_unref (key_value_pairs);
+
+ if (ret)
+ {
+ g_assert (this_error == NULL);
+ goto out;
+ }
+ }
+
+ if (this_error != NULL)
+ {
+ if (last_error != NULL)
+ g_error_free (last_error);
+ last_error = this_error;
+ }
+ }
+
+ if (!ret)
+ goto out;
+
+ out:
+ if (ret)
+ {
+ if (last_error != NULL)
+ g_error_free (last_error);
+ }
+ else
+ {
+ g_assert (last_error != NULL);
+ g_propagate_error (error, last_error);
+ }
+ return ret;
+}
+
+
+static void
+initable_iface_init (GInitableIface *initable_iface)
+{
+ initable_iface->init = initable_init;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */