* 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.
+ * version 2.1 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
* 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
-#ifdef _WIN32
-#include <io.h>
-#endif
#include "giotypes.h"
#include "gioerror.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"
+#include "gmarshal-internal.h"
+
+#ifdef G_OS_UNIX
+#include <unistd.h>
+#endif
+#ifdef G_OS_WIN32
+#include <io.h>
+#endif
#ifdef G_OS_UNIX
#include "gunixsocketaddress.h"
* 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().
*
- * <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>
+ * 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).
+ *
+ * Note that a minimal #GDBusServer will accept connections from any
+ * peer. In many use-cases it will be necessary to add a #GDBusAuthObserver
+ * that only accepts connections that have successfully authenticated
+ * as the same user that is running the #GDBusServer.
*/
/**
gchar *client_address;
+ gchar *unix_socket_path;
GSocketListener *listener;
gboolean is_using_listener;
gulong run_signal_handler_id;
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)
- );
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init))
+
+static void
+g_dbus_server_dispose (GObject *object)
+{
+ GDBusServer *server = G_DBUS_SERVER (object);
+
+ if (server->active)
+ g_dbus_server_stop (server);
+
+ G_OBJECT_CLASS (g_dbus_server_parent_class)->dispose (object);
+}
static void
g_dbus_server_finalize (GObject *object)
{
GDBusServer *server = G_DBUS_SERVER (object);
+ g_assert (!server->active);
+
if (server->authentication_observer != NULL)
g_object_unref (server->authentication_observer);
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->unix_socket_path);
g_free (server->nonce_file);
g_main_context_unref (server->main_context_at_construction);
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->dispose = g_dbus_server_dispose;
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;
*
* 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
*
* Since: 2.26
*/
- _signals[NEW_CONNECTION_SIGNAL] = g_signal_new ("new-connection",
+ _signals[NEW_CONNECTION_SIGNAL] = g_signal_new (I_("new-connection"),
G_TYPE_DBUS_SERVER,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GDBusServerClass, new_connection),
g_signal_accumulator_true_handled,
NULL, /* accu_data */
- NULL,
+ _g_cclosure_marshal_BOOLEAN__OBJECT,
G_TYPE_BOOLEAN,
1,
G_TYPE_DBUS_CONNECTION);
+ g_signal_set_va_marshaller (_signals[NEW_CONNECTION_SIGNAL],
+ G_TYPE_FROM_CLASS (klass),
+ _g_cclosure_marshal_BOOLEAN__OBJECTv);
}
static void
* @address: A D-Bus address.
* @flags: Flags from the #GDBusServerFlags enumeration.
* @guid: A D-Bus GUID.
- * @observer: (allow-none): A #GDBusAuthObserver or %NULL.
- * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @observer: (nullable): A #GDBusAuthObserver or %NULL.
+ * @cancellable: (nullable): A #GCancellable or %NULL.
* @error: Return location for server or %NULL.
*
* Creates a new D-Bus server that listens on the first address in
* Once constructed, you can use g_dbus_server_get_client_address() to
* get a D-Bus address string that clients can use to connect.
*
+ * To have control over the available authentication mechanisms and
+ * the users that are authorized to connect, it is strongly recommended
+ * to provide a non-%NULL #GDBusAuthObserver.
+ *
* Connect to the #GDBusServer::new-connection signal to handle
* incoming connections.
*
* 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.
+ * This is a synchronous failable constructor. There is currently no
+ * asynchronous version.
*
* Returns: A #GDBusServer or %NULL if @error is set. Free with
* g_object_unref().
* 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.
+ * Gets a
+ * [D-Bus address](https://dbus.freedesktop.org/doc/dbus-specification.html#addresses)
+ * 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.
return;
/* 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_data (G_SOCKET_SERVICE (server->listener),
+ "run",
+ G_CALLBACK (on_run),
+ g_object_ref (server),
+ (GClosureNotify) g_object_unref,
+ 0 /* flags */);
g_socket_service_start (G_SOCKET_SERVICE (server->listener));
server->active = TRUE;
g_object_notify (G_OBJECT (server), "active");
/* Right now we don't have any transport not using the listener... */
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_clear_signal_handler (&server->run_signal_handler_id, server->listener);
g_socket_service_stop (G_SOCKET_SERVICE (server->listener));
server->active = FALSE;
g_object_notify (G_OBJECT (server), "active");
+
+ if (server->unix_socket_path)
+ {
+ if (g_unlink (server->unix_socket_path) != 0)
+ g_warning ("Failed to delete %s: %s", server->unix_socket_path, g_strerror (errno));
+ }
+
+ if (server->nonce_file)
+ {
+ if (g_unlink (server->nonce_file) != 0)
+ g_warning ("Failed to delete %s: %s", server->nonce_file, g_strerror (errno));
+ }
}
/* ---------------------------------------------------------------------------------------------------- */
return ret;
}
-/* note that address_entry has already been validated => exactly one of path, tmpdir or abstract keys are set */
+/* note that address_entry has already been validated => exactly one of path, dir, tmpdir, or abstract keys are set */
static gboolean
try_unix (GDBusServer *server,
const gchar *address_entry,
{
gboolean ret;
const gchar *path;
+ const gchar *dir;
const gchar *tmpdir;
const gchar *abstract;
GSocketAddress *address;
address = NULL;
path = g_hash_table_lookup (key_value_pairs, "path");
+ dir = g_hash_table_lookup (key_value_pairs, "dir");
tmpdir = g_hash_table_lookup (key_value_pairs, "tmpdir");
abstract = g_hash_table_lookup (key_value_pairs, "abstract");
{
address = g_unix_socket_address_new (path);
}
- else if (tmpdir != NULL)
+ else if (dir != NULL || tmpdir != NULL)
{
gint n;
GString *s;
GError *local_error;
retry:
- s = g_string_new (tmpdir);
+ s = g_string_new (tmpdir != NULL ? tmpdir : dir);
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 ())
+ /* prefer abstract namespace if available for tmpdir: addresses
+ * abstract namespace is disallowed for dir: addresses */
+ if (tmpdir != NULL && g_unix_socket_address_abstract_names_supported ())
address = g_unix_socket_address_new_with_type (s->str,
-1,
G_UNIX_SOCKET_ADDRESS_ABSTRACT);
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
- _("Abstract name space not supported"));
+ _("Abstract namespace not supported"));
goto out;
}
address = g_unix_socket_address_new_with_type (abstract,
/* Fill out client_address if the connection attempt worked */
if (ret)
{
+ const char *address_path;
+ char *escaped_path;
+
server->is_using_listener = TRUE;
+ address_path = g_unix_socket_address_get_path (G_UNIX_SOCKET_ADDRESS (address));
+ escaped_path = g_dbus_address_escape_value (address_path);
switch (g_unix_socket_address_get_address_type (G_UNIX_SOCKET_ADDRESS (address)))
{
case G_UNIX_SOCKET_ADDRESS_ABSTRACT:
- server->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", escaped_path);
break;
case G_UNIX_SOCKET_ADDRESS_PATH:
- server->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", escaped_path);
+ server->unix_socket_path = g_strdup (address_path);
break;
default:
g_assert_not_reached ();
break;
}
+
+ g_free (escaped_path);
}
g_object_unref (address);
}
gsize bytes_written;
gsize bytes_remaining;
char *file_escaped;
+ char *host_escaped;
server->nonce = g_new0 (guchar, 16);
for (n = 0; n < 16; n++)
while (bytes_remaining > 0)
{
gssize ret;
+ int errsv;
+
ret = write (fd, server->nonce + bytes_written, bytes_remaining);
+ errsv = errno;
if (ret == -1)
{
- if (errno == EINTR)
+ if (errsv == EINTR)
goto again;
g_set_error (error,
G_IO_ERROR,
- g_io_error_from_errno (errno),
- _("Error writing nonce file at `%s': %s"),
+ g_io_error_from_errno (errsv),
+ _("Error writing nonce file at “%s”: %s"),
server->nonce_file,
- strerror (errno));
+ g_strerror (errsv));
goto out;
}
bytes_written += ret;
bytes_remaining -= ret;
}
- close (fd);
- file_escaped = g_uri_escape_string (server->nonce_file, "/\\", FALSE);
+ if (!g_close (fd, error))
+ goto out;
+ host_escaped = g_dbus_address_escape_value (host);
+ file_escaped = g_dbus_address_escape_value (server->nonce_file);
server->client_address = g_strdup_printf ("nonce-tcp:host=%s,port=%d,noncefile=%s",
- host,
+ host_escaped,
port_num,
file_escaped);
+ g_free (host_escaped);
g_free (file_escaped);
}
else
out:
g_list_free_full (resolved_addresses, g_object_unref);
- g_object_unref (resolver);
+ if (resolver)
+ g_object_unref (resolver);
return ret;
}
emit_new_connection_in_idle,
data,
(GDestroyNotify) emit_idle_data_free);
+ 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);
}
g_set_error (&last_error,
G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT,
- _("The string `%s' is not a valid D-Bus GUID"),
+ _("The string “%s” is not a valid D-Bus GUID"),
server->guid);
goto out;
}
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)
{
- 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);
+ g_clear_error (&last_error);
}
else
{