* 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.
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include <stdlib.h>
#include <string.h>
#include <errno.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
#include "giotypes.h"
#include "gioerror.h"
#include "gioenumtypes.h"
#include "gdbusprivate.h"
#include "gdbusauthobserver.h"
-#include "gio-marshal.h"
#include "ginitable.h"
#include "gsocketservice.h"
#include "gthreadedsocketservice.h"
#include "gresolver.h"
+#include "glib/gstdio.h"
#include "ginetaddress.h"
#include "ginetsocketaddress.h"
#include "ginputstream.h"
#include "giostream.h"
#ifdef G_OS_UNIX
+#include <unistd.h>
+#endif
+#ifdef G_OS_WIN32
+#include <io.h>
+#endif
+
+#ifdef G_OS_UNIX
#include "gunixsocketaddress.h"
#endif
#include "glibintl.h"
-#include "gioalias.h"
/**
* SECTION:gdbusserver
* @include: gio/gio.h
*
* #GDBusServer is a helper for listening to and accepting D-Bus
- * connections.
+ * connections. This can be used to create a new D-Bus server, allowing two
+ * peers to use the D-Bus protocol for their own specialized communication.
+ * A server instance provided in this way will not perform message routing or
+ * implement the org.freedesktop.DBus interface.
*
- * <example id="gdbus-peer-to-peer"><title>D-Bus peer-to-peer example</title><programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../gio/tests/gdbus-example-peer.c"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting></example>
+ * To just export an object on a well-known name on a message bus, such as the
+ * session or system bus, you should instead use g_bus_own_name().
+ *
+ * An example of peer-to-peer communication with G-DBus can be found
+ * in [gdbus-example-peer.c](https://git.gnome.org/browse/glib/tree/gio/tests/gdbus-example-peer.c).
*/
-struct _GDBusServerPrivate
+/**
+ * GDBusServer:
+ *
+ * The #GDBusServer structure contains only private data and
+ * should only be accessed using the provided API.
+ *
+ * Since: 2.26
+ */
+struct _GDBusServer
{
+ /*< private >*/
+ GObject parent_instance;
+
GDBusServerFlags flags;
gchar *address;
gchar *guid;
GSocketListener *listener;
gboolean is_using_listener;
+ gulong run_signal_handler_id;
- /* The result of g_main_context_get_thread_default() when the object
+ /* The result of g_main_context_ref_thread_default() when the object
* was created (the GObject _init() function) - this is used for delivery
* of the :new-connection GObject signal.
*/
GDBusAuthObserver *authentication_observer;
};
+typedef struct _GDBusServerClass GDBusServerClass;
+
+/**
+ * GDBusServerClass:
+ * @new_connection: Signal class handler for the #GDBusServer::new-connection signal.
+ *
+ * Class structure for #GDBusServer.
+ *
+ * Since: 2.26
+ */
+struct _GDBusServerClass
+{
+ /*< private >*/
+ GObjectClass parent_class;
+
+ /*< public >*/
+ /* Signals */
+ gboolean (*new_connection) (GDBusServer *server,
+ GDBusConnection *connection);
+};
+
enum
{
PROP_0,
LAST_SIGNAL,
};
-guint _signals[LAST_SIGNAL] = {0};
+static guint _signals[LAST_SIGNAL] = {0};
static void initable_iface_init (GInitableIface *initable_iface);
{
GDBusServer *server = G_DBUS_SERVER (object);
- if (server->priv->authentication_observer != NULL)
- g_object_unref (server->priv->authentication_observer);
+ if (server->authentication_observer != NULL)
+ g_object_unref (server->authentication_observer);
+
+ if (server->run_signal_handler_id > 0)
+ g_signal_handler_disconnect (server->listener, server->run_signal_handler_id);
- if (server->priv->listener != NULL)
- g_object_unref (server->priv->listener);
+ if (server->listener != NULL)
+ g_object_unref (server->listener);
- g_free (server->priv->address);
- g_free (server->priv->guid);
- g_free (server->priv->client_address);
- if (server->priv->nonce != NULL)
+ g_free (server->address);
+ g_free (server->guid);
+ g_free (server->client_address);
+ if (server->nonce != NULL)
{
- memset (server->priv->nonce, '\0', 16);
- g_free (server->priv->nonce);
+ memset (server->nonce, '\0', 16);
+ g_free (server->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);
+ g_free (server->nonce_file);
- if (server->priv->main_context_at_construction != NULL)
- g_main_context_unref (server->priv->main_context_at_construction);
+ g_main_context_unref (server->main_context_at_construction);
G_OBJECT_CLASS (g_dbus_server_parent_class)->finalize (object);
}
switch (prop_id)
{
case PROP_FLAGS:
- g_value_set_flags (value, server->priv->flags);
+ g_value_set_flags (value, server->flags);
break;
case PROP_GUID:
- g_value_set_string (value, server->priv->guid);
+ g_value_set_string (value, server->guid);
break;
case PROP_ADDRESS:
- g_value_set_string (value, server->priv->address);
+ g_value_set_string (value, server->address);
break;
case PROP_CLIENT_ADDRESS:
- g_value_set_string (value, server->priv->client_address);
+ g_value_set_string (value, server->client_address);
break;
case PROP_ACTIVE:
- g_value_set_boolean (value, server->priv->active);
+ g_value_set_boolean (value, server->active);
break;
case PROP_AUTHENTICATION_OBSERVER:
- g_value_set_object (value, server->priv->authentication_observer);
+ g_value_set_object (value, server->authentication_observer);
break;
default:
switch (prop_id)
{
case PROP_FLAGS:
- server->priv->flags = g_value_get_flags (value);
+ server->flags = g_value_get_flags (value);
break;
case PROP_GUID:
- server->priv->guid = g_value_dup_string (value);
+ server->guid = g_value_dup_string (value);
break;
case PROP_ADDRESS:
- server->priv->address = g_value_dup_string (value);
+ server->address = g_value_dup_string (value);
break;
case PROP_AUTHENTICATION_OBSERVER:
- server->priv->authentication_observer = g_value_dup_object (value);
+ server->authentication_observer = g_value_dup_object (value);
break;
default:
*/
g_object_class_install_property (gobject_class,
PROP_ACTIVE,
- g_param_spec_string ("active",
- P_("Active"),
- P_("Whether the server is currently active"),
- NULL,
- G_PARAM_READABLE |
- G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB |
- G_PARAM_STATIC_NICK));
+ g_param_spec_boolean ("active",
+ P_("Active"),
+ P_("Whether the server is currently active"),
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB |
+ G_PARAM_STATIC_NICK));
/**
* GDBusServer:authentication-observer:
* g_dbus_connection_get_peer_credentials() to figure out what
* identity (if any), was authenticated.
*
- * If you want to accept the connection, simply ref the @connection
- * object. Then call g_dbus_connection_close() and unref it when you
- * are done with it. A typical thing to do when accepting a
- * connection is to listen to the #GDBusConnection::closed signal.
+ * If you want to accept the connection, take a reference to the
+ * @connection object and return %TRUE. When you are done with the
+ * connection call g_dbus_connection_close() and give up your
+ * reference. Note that the other peer may disconnect at any time -
+ * a typical thing to do when accepting a connection is to listen to
+ * the #GDBusConnection::closed signal.
*
* 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 that @server was constructed in.
+ * connection. Otherwise the signal is emitted in the
+ * [thread-default main context][g-main-context-push-thread-default]
+ * of the thread that @server was constructed in.
+ *
+ * You are guaranteed that signal handlers for this signal runs
+ * before incoming messages on @connection are processed. This means
+ * that it's suitable to call g_dbus_connection_register_object() or
+ * similar from the signal handler.
+ *
+ * Returns: %TRUE to claim @connection, %FALSE to let other handlers
+ * run.
*
* Since: 2.26
*/
G_TYPE_DBUS_SERVER,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GDBusServerClass, new_connection),
+ g_signal_accumulator_true_handled,
+ NULL, /* accu_data */
NULL,
- NULL,
- g_cclosure_marshal_VOID__OBJECT,
- G_TYPE_NONE,
+ G_TYPE_BOOLEAN,
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);
+ server->main_context_at_construction = g_main_context_ref_thread_default ();
}
static gboolean
* @address: A D-Bus address.
* @flags: Flags from the #GDBusServerFlags enumeration.
* @guid: A D-Bus GUID.
- * @observer: A #GDBusAuthObserver or %NULL.
- * @cancellable: A #GCancellable or %NULL.
+ * @observer: (allow-none): A #GDBusAuthObserver or %NULL.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
* @error: Return location for server or %NULL.
*
* Creates a new D-Bus server that listens on the first address in
* The returned #GDBusServer isn't active - you have to start it with
* g_dbus_server_start().
*
- * See <xref linkend="gdbus-peer-to-peer"/> for how #GDBusServer can
- * be used.
+ * #GDBusServer is used in this [example][gdbus-peer-to-peer].
*
* This is a synchronous failable constructor. See
* g_dbus_server_new() for the asynchronous version.
"guid", guid,
"authentication-observer", observer,
NULL);
- if (server != NULL)
- {
- /* Right now we don't have any transport not using the listener... */
- g_assert (server->priv->is_using_listener);
- g_signal_connect (G_SOCKET_SERVICE (server->priv->listener),
- "run",
- G_CALLBACK (on_run),
- server);
- }
return server;
}
g_dbus_server_get_client_address (GDBusServer *server)
{
g_return_val_if_fail (G_IS_DBUS_SERVER (server), NULL);
- return server->priv->client_address;
+ return server->client_address;
}
/**
g_dbus_server_get_guid (GDBusServer *server)
{
g_return_val_if_fail (G_IS_DBUS_SERVER (server), NULL);
- return server->priv->guid;
+ return server->guid;
}
/**
g_dbus_server_get_flags (GDBusServer *server)
{
g_return_val_if_fail (G_IS_DBUS_SERVER (server), G_DBUS_SERVER_FLAGS_NONE);
- return server->priv->flags;
+ return server->flags;
}
/**
g_dbus_server_is_active (GDBusServer *server)
{
g_return_val_if_fail (G_IS_DBUS_SERVER (server), G_DBUS_SERVER_FLAGS_NONE);
- return server->priv->active;
+ return server->active;
}
/**
g_dbus_server_start (GDBusServer *server)
{
g_return_if_fail (G_IS_DBUS_SERVER (server));
- if (server->priv->active)
+ if (server->active)
return;
/* 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));
- server->priv->active = TRUE;
+ g_assert (server->is_using_listener);
+ g_socket_service_start (G_SOCKET_SERVICE (server->listener));
+ server->active = TRUE;
g_object_notify (G_OBJECT (server), "active");
}
g_dbus_server_stop (GDBusServer *server)
{
g_return_if_fail (G_IS_DBUS_SERVER (server));
- if (!server->priv->active)
+ if (!server->active)
return;
/* Right now we don't have any transport not using the listener... */
- g_assert (server->priv->is_using_listener);
- g_socket_service_stop (G_SOCKET_SERVICE (server->priv->listener));
- server->priv->active = FALSE;
+ g_assert (server->is_using_listener);
+ g_assert (server->run_signal_handler_id > 0);
+ g_signal_handler_disconnect (server->listener, server->run_signal_handler_id);
+ server->run_signal_handler_id = 0;
+ g_socket_service_stop (G_SOCKET_SERVICE (server->listener));
+ server->active = FALSE;
g_object_notify (G_OBJECT (server), "active");
}
g_string_free (s, TRUE);
local_error = NULL;
- if (!g_socket_listener_add_address (server->priv->listener,
+ if (!g_socket_listener_add_address (server->listener,
address,
G_SOCKET_TYPE_STREAM,
G_SOCKET_PROTOCOL_DEFAULT,
g_assert_not_reached ();
}
- if (!g_socket_listener_add_address (server->priv->listener,
+ if (!g_socket_listener_add_address (server->listener,
address,
G_SOCKET_TYPE_STREAM,
G_SOCKET_PROTOCOL_DEFAULT,
/* Fill out client_address if the connection attempt worked */
if (ret)
{
- server->priv->is_using_listener = TRUE;
+ server->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)));
+ server->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)));
+ server->client_address = g_strdup_printf ("unix:path=%s",
+ g_unix_socket_address_get_path (G_UNIX_SOCKET_ADDRESS (address)));
break;
default:
/* ---------------------------------------------------------------------------------------------------- */
/* 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)
+ * both host and port (guaranteed to be a number in [0, 65535]) are set (family is optional)
*/
static gboolean
try_tcp (GDBusServer *server,
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");
+ /* family = g_hash_table_lookup (key_value_pairs, "family"); */
if (g_hash_table_lookup (key_value_pairs, "noncefile") != NULL)
{
g_set_error_literal (error,
GSocketAddress *effective_address;
socket_address = g_inet_socket_address_new (address, port_num);
- if (!g_socket_listener_add_address (server->priv->listener,
+ if (!g_socket_listener_add_address (server->listener,
socket_address,
G_SOCKET_TYPE_STREAM,
G_SOCKET_PROTOCOL_TCP,
guint n;
gsize bytes_written;
gsize bytes_remaining;
+ char *file_escaped;
- server->priv->nonce = g_new0 (guchar, 16);
+ server->nonce = g_new0 (guchar, 16);
for (n = 0; n < 16; n++)
- server->priv->nonce[n] = g_random_int_range (0, 256);
+ server->nonce[n] = g_random_int_range (0, 256);
fd = g_file_open_tmp ("gdbus-nonce-file-XXXXXX",
- &server->priv->nonce_file,
+ &server->nonce_file,
error);
if (fd == -1)
{
- g_socket_listener_close (server->priv->listener);
+ g_socket_listener_close (server->listener);
goto out;
}
again:
while (bytes_remaining > 0)
{
gssize ret;
- ret = write (fd, server->priv->nonce + bytes_written, bytes_remaining);
+ ret = write (fd, server->nonce + bytes_written, bytes_remaining);
if (ret == -1)
{
if (errno == EINTR)
g_set_error (error,
G_IO_ERROR,
g_io_error_from_errno (errno),
- _("Error writing nonce file at `%s': %s"),
- server->priv->nonce_file,
+ _("Error writing nonce file at '%s': %s"),
+ server->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);
+ if (!g_close (fd, error))
+ goto out;
+ file_escaped = g_uri_escape_string (server->nonce_file, "/\\", FALSE);
+ server->client_address = g_strdup_printf ("nonce-tcp:host=%s,port=%d,noncefile=%s",
+ host,
+ port_num,
+ file_escaped);
+ g_free (file_escaped);
}
else
{
- server->priv->client_address = g_strdup_printf ("tcp:host=%s,port=%d", host, port_num);
+ server->client_address = g_strdup_printf ("tcp:host=%s,port=%d", host, port_num);
}
- server->priv->is_using_listener = TRUE;
+ server->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);
+ g_list_free_full (resolved_addresses, g_object_unref);
+ if (resolver)
+ g_object_unref (resolver);
return ret;
}
emit_new_connection_in_idle (gpointer user_data)
{
EmitIdleData *data = user_data;
+ gboolean claimed;
+ claimed = FALSE;
g_signal_emit (data->server,
_signals[NEW_CONNECTION_SIGNAL],
0,
- data->connection);
+ data->connection,
+ &claimed);
+
+ if (claimed)
+ g_dbus_connection_start_message_processing (data->connection);
g_object_unref (data->connection);
return FALSE;
GDBusConnection *connection;
GDBusConnectionFlags connection_flags;
- if (server->priv->nonce != NULL)
+ if (server->nonce != NULL)
{
gchar buf[16];
gsize bytes_read;
if (bytes_read != 16)
goto out;
- if (memcmp (buf, server->priv->nonce, 16) != 0)
+ if (memcmp (buf, server->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_SERVER |
+ G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING;
+ if (server->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,
+ server->guid,
connection_flags,
- server->priv->authentication_observer,
+ server->authentication_observer,
NULL, /* GCancellable */
NULL); /* GError */
if (connection == NULL)
goto out;
- if (server->priv->flags & G_DBUS_SERVER_FLAGS_RUN_IN_THREAD)
+ if (server->flags & G_DBUS_SERVER_FLAGS_RUN_IN_THREAD)
{
+ gboolean claimed;
+
+ claimed = FALSE;
g_signal_emit (server,
_signals[NEW_CONNECTION_SIGNAL],
0,
- connection);
+ connection,
+ &claimed);
+ if (claimed)
+ g_dbus_connection_start_message_processing (connection);
g_object_unref (connection);
}
else
emit_new_connection_in_idle,
data,
(GDestroyNotify) emit_idle_data_free);
- g_source_attach (idle_source, server->priv->main_context_at_construction);
+ g_source_set_name (idle_source, "[gio] emit_new_connection_in_idle");
+ g_source_attach (idle_source, server->main_context_at_construction);
g_source_unref (idle_source);
}
GError *last_error;
ret = FALSE;
+ addr_array = NULL;
last_error = NULL;
- if (!g_dbus_is_guid (server->priv->guid))
+ if (!g_dbus_is_guid (server->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);
+ _("The string '%s' is not a valid D-Bus GUID"),
+ server->guid);
goto out;
}
- server->priv->listener = G_SOCKET_LISTENER (g_threaded_socket_service_new (-1));
+ server->listener = G_SOCKET_LISTENER (g_threaded_socket_service_new (-1));
- addr_array = g_strsplit (server->priv->address, ";", 0);
+ addr_array = g_strsplit (server->address, ";", 0);
last_error = NULL;
for (n = 0; addr_array != NULL && addr_array[n] != NULL; n++)
{
g_set_error (&this_error,
G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT,
- _("Cannot listen on unsupported transport `%s'"),
+ _("Cannot listen on unsupported transport '%s'"),
transport_name);
g_free (transport_name);
}
}
- if (!ret)
- goto out;
-
out:
+
+ g_strfreev (addr_array);
+
if (ret)
{
if (last_error != NULL)
g_error_free (last_error);
+
+ /* Right now we don't have any transport not using the listener... */
+ g_assert (server->is_using_listener);
+ server->run_signal_handler_id = g_signal_connect (G_SOCKET_SERVICE (server->listener),
+ "run",
+ G_CALLBACK (on_run),
+ server);
}
else
{
}
/* ---------------------------------------------------------------------------------------------------- */
-
-#define __G_DBUS_SERVER_C__
-#include "gioaliasdef.c"