mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-26 15:36:14 +01:00
0f2e4fd01c
The two known use-cases for autolaunching are: * X-forwarding: "ssh -Y myhost myapp" resulting in a session bus on myhost but an X server on the original host * Legacy desktop environments on OSs without D-Bus integration: e.g. running a single GNOME or KDE app under fvwm or something, without a session dbus-daemon being started by either systemd, gnome-session, or OS integration scripts analogous to Debian's /etc/X11/Xsession.d/75dbus_dbus-launch In either case, an X11 DISPLAY is also needed. "dbus-launch --autolaunch" doesn't do anything useful when unable to connect to an X11 display; this has been the case since the feature was added in 2006, and is useful to avoid "split brain" situations in which two processes that ought to be part of the same session end up on separate session buses. Since dbus commit 407c111 in 2011, libdbus hasn't even attempted to run "dbus-launch --autolaunch" unless getenv("DISPLAY") returns non-null in the parent: this avoids doing a relatively complicated fork-and-exec that is clearly not going to lead to success. This commit gives GDBus the same policy. This change was originally made to work around a race condition in subprocess spawning (Debian bug #737380, GNOME bug #711090) but it seems valid in its own right. In my opinion as D-Bus maintainer, "dbus-launch --autolaunch" should be considered to be an X11 feature, and any future D-Bus enhancements (e.g. kdbus) or successors for X11 (e.g. Wayland, Mir) should obtain a session bus address by other means - either a session manager such as "systemd --user", gnome-session or Upstart, or a wrapper for the user session like dbus-run-session(1). Related to dbus bug <https://bugs.freedesktop.org/show_bug.cgi?id=19997>. Bug: https://bugzilla.gnome.org/show_bug.cgi?id=723506 Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=737380 Signed-off-by: Simon McVittie <simon.mcvittie@collabora.co.uk>
1730 lines
47 KiB
C
1730 lines
47 KiB
C
/* GDBus - GLib D-Bus Library
|
||
*
|
||
* Copyright (C) 2008-2010 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, see <http://www.gnu.org/licenses/>.
|
||
*
|
||
* Author: David Zeuthen <davidz@redhat.com>
|
||
*/
|
||
|
||
#include "config.h"
|
||
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <stdio.h>
|
||
#include <errno.h>
|
||
|
||
#include "gioerror.h"
|
||
#include "gdbusutils.h"
|
||
#include "gdbusaddress.h"
|
||
#include "gdbuserror.h"
|
||
#include "gioenumtypes.h"
|
||
#include "gnetworkaddress.h"
|
||
#include "gsocketclient.h"
|
||
#include "giostream.h"
|
||
#include "gasyncresult.h"
|
||
#include "gtask.h"
|
||
#include "glib-private.h"
|
||
#include "gdbusprivate.h"
|
||
#include "giomodule-priv.h"
|
||
#include "gdbusdaemon.h"
|
||
#include "gstdio.h"
|
||
|
||
#ifdef G_OS_UNIX
|
||
#include <unistd.h>
|
||
#include <sys/stat.h>
|
||
#include <sys/types.h>
|
||
#include <gio/gunixsocketaddress.h>
|
||
#endif
|
||
|
||
#ifdef G_OS_WIN32
|
||
#include <windows.h>
|
||
#include <io.h>
|
||
#include <conio.h>
|
||
#endif
|
||
|
||
#include "glibintl.h"
|
||
|
||
/**
|
||
* SECTION:gdbusaddress
|
||
* @title: D-Bus Addresses
|
||
* @short_description: D-Bus connection endpoints
|
||
* @include: gio/gio.h
|
||
*
|
||
* Routines for working with D-Bus addresses. A D-Bus address is a string
|
||
* like "unix:tmpdir=/tmp/my-app-name". The exact format of addresses
|
||
* is explained in detail in the [D-Bus specification](http://dbus.freedesktop.org/doc/dbus-specification.html\#addresses).
|
||
*/
|
||
|
||
static gchar *get_session_address_platform_specific (GError **error);
|
||
static gchar *get_session_address_dbus_launch (GError **error);
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
/**
|
||
* g_dbus_is_address:
|
||
* @string: A string.
|
||
*
|
||
* Checks if @string is a D-Bus address.
|
||
*
|
||
* This doesn't check if @string is actually supported by #GDBusServer
|
||
* or #GDBusConnection - use g_dbus_is_supported_address() to do more
|
||
* checks.
|
||
*
|
||
* Returns: %TRUE if @string is a valid D-Bus address, %FALSE otherwise.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
gboolean
|
||
g_dbus_is_address (const gchar *string)
|
||
{
|
||
guint n;
|
||
gchar **a;
|
||
gboolean ret;
|
||
|
||
ret = FALSE;
|
||
|
||
g_return_val_if_fail (string != NULL, FALSE);
|
||
|
||
a = g_strsplit (string, ";", 0);
|
||
if (a[0] == NULL)
|
||
goto out;
|
||
|
||
for (n = 0; a[n] != NULL; n++)
|
||
{
|
||
if (!_g_dbus_address_parse_entry (a[n],
|
||
NULL,
|
||
NULL,
|
||
NULL))
|
||
goto out;
|
||
}
|
||
|
||
ret = TRUE;
|
||
|
||
out:
|
||
g_strfreev (a);
|
||
return ret;
|
||
}
|
||
|
||
static gboolean
|
||
is_valid_unix (const gchar *address_entry,
|
||
GHashTable *key_value_pairs,
|
||
GError **error)
|
||
{
|
||
gboolean ret;
|
||
GList *keys;
|
||
GList *l;
|
||
const gchar *path;
|
||
const gchar *tmpdir;
|
||
const gchar *abstract;
|
||
|
||
ret = FALSE;
|
||
keys = NULL;
|
||
path = NULL;
|
||
tmpdir = NULL;
|
||
abstract = NULL;
|
||
|
||
keys = g_hash_table_get_keys (key_value_pairs);
|
||
for (l = keys; l != NULL; l = l->next)
|
||
{
|
||
const gchar *key = l->data;
|
||
if (g_strcmp0 (key, "path") == 0)
|
||
path = g_hash_table_lookup (key_value_pairs, key);
|
||
else if (g_strcmp0 (key, "tmpdir") == 0)
|
||
tmpdir = g_hash_table_lookup (key_value_pairs, key);
|
||
else if (g_strcmp0 (key, "abstract") == 0)
|
||
abstract = g_hash_table_lookup (key_value_pairs, key);
|
||
else
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Unsupported key '%s' in address entry '%s'"),
|
||
key,
|
||
address_entry);
|
||
goto out;
|
||
}
|
||
}
|
||
|
||
if (path != NULL)
|
||
{
|
||
if (tmpdir != NULL || abstract != NULL)
|
||
goto meaningless;
|
||
}
|
||
else if (tmpdir != NULL)
|
||
{
|
||
if (path != NULL || abstract != NULL)
|
||
goto meaningless;
|
||
}
|
||
else if (abstract != NULL)
|
||
{
|
||
if (path != NULL || tmpdir != NULL)
|
||
goto meaningless;
|
||
}
|
||
else
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Address '%s' is invalid (need exactly one of path, tmpdir or abstract keys)"),
|
||
address_entry);
|
||
goto out;
|
||
}
|
||
|
||
|
||
ret= TRUE;
|
||
goto out;
|
||
|
||
meaningless:
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Meaningless key/value pair combination in address entry '%s'"),
|
||
address_entry);
|
||
|
||
out:
|
||
g_list_free (keys);
|
||
|
||
return ret;
|
||
}
|
||
|
||
static gboolean
|
||
is_valid_nonce_tcp (const gchar *address_entry,
|
||
GHashTable *key_value_pairs,
|
||
GError **error)
|
||
{
|
||
gboolean ret;
|
||
GList *keys;
|
||
GList *l;
|
||
const gchar *host;
|
||
const gchar *port;
|
||
const gchar *family;
|
||
const gchar *nonce_file;
|
||
gint port_num;
|
||
gchar *endp;
|
||
|
||
ret = FALSE;
|
||
keys = NULL;
|
||
host = NULL;
|
||
port = NULL;
|
||
family = NULL;
|
||
nonce_file = NULL;
|
||
|
||
keys = g_hash_table_get_keys (key_value_pairs);
|
||
for (l = keys; l != NULL; l = l->next)
|
||
{
|
||
const gchar *key = l->data;
|
||
if (g_strcmp0 (key, "host") == 0)
|
||
host = g_hash_table_lookup (key_value_pairs, key);
|
||
else if (g_strcmp0 (key, "port") == 0)
|
||
port = g_hash_table_lookup (key_value_pairs, key);
|
||
else if (g_strcmp0 (key, "family") == 0)
|
||
family = g_hash_table_lookup (key_value_pairs, key);
|
||
else if (g_strcmp0 (key, "noncefile") == 0)
|
||
nonce_file = g_hash_table_lookup (key_value_pairs, key);
|
||
else
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Unsupported key '%s' in address entry '%s'"),
|
||
key,
|
||
address_entry);
|
||
goto out;
|
||
}
|
||
}
|
||
|
||
if (port != NULL)
|
||
{
|
||
port_num = strtol (port, &endp, 10);
|
||
if ((*port == '\0' || *endp != '\0') || port_num < 0 || port_num >= 65536)
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Error in address '%s' - the port attribute is malformed"),
|
||
address_entry);
|
||
goto out;
|
||
}
|
||
}
|
||
|
||
if (family != NULL && !(g_strcmp0 (family, "ipv4") == 0 || g_strcmp0 (family, "ipv6") == 0))
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Error in address '%s' - the family attribute is malformed"),
|
||
address_entry);
|
||
goto out;
|
||
}
|
||
|
||
if (host != NULL)
|
||
{
|
||
/* TODO: validate host */
|
||
}
|
||
|
||
nonce_file = nonce_file; /* To avoid -Wunused-but-set-variable */
|
||
|
||
ret= TRUE;
|
||
|
||
out:
|
||
g_list_free (keys);
|
||
|
||
return ret;
|
||
}
|
||
|
||
static gboolean
|
||
is_valid_tcp (const gchar *address_entry,
|
||
GHashTable *key_value_pairs,
|
||
GError **error)
|
||
{
|
||
gboolean ret;
|
||
GList *keys;
|
||
GList *l;
|
||
const gchar *host;
|
||
const gchar *port;
|
||
const gchar *family;
|
||
gint port_num;
|
||
gchar *endp;
|
||
|
||
ret = FALSE;
|
||
keys = NULL;
|
||
host = NULL;
|
||
port = NULL;
|
||
family = NULL;
|
||
|
||
keys = g_hash_table_get_keys (key_value_pairs);
|
||
for (l = keys; l != NULL; l = l->next)
|
||
{
|
||
const gchar *key = l->data;
|
||
if (g_strcmp0 (key, "host") == 0)
|
||
host = g_hash_table_lookup (key_value_pairs, key);
|
||
else if (g_strcmp0 (key, "port") == 0)
|
||
port = g_hash_table_lookup (key_value_pairs, key);
|
||
else if (g_strcmp0 (key, "family") == 0)
|
||
family = g_hash_table_lookup (key_value_pairs, key);
|
||
else
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Unsupported key '%s' in address entry '%s'"),
|
||
key,
|
||
address_entry);
|
||
goto out;
|
||
}
|
||
}
|
||
|
||
if (port != NULL)
|
||
{
|
||
port_num = strtol (port, &endp, 10);
|
||
if ((*port == '\0' || *endp != '\0') || port_num < 0 || port_num >= 65536)
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Error in address '%s' - the port attribute is malformed"),
|
||
address_entry);
|
||
goto out;
|
||
}
|
||
}
|
||
|
||
if (family != NULL && !(g_strcmp0 (family, "ipv4") == 0 || g_strcmp0 (family, "ipv6") == 0))
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Error in address '%s' - the family attribute is malformed"),
|
||
address_entry);
|
||
goto out;
|
||
}
|
||
|
||
if (host != NULL)
|
||
{
|
||
/* TODO: validate host */
|
||
}
|
||
|
||
ret= TRUE;
|
||
|
||
out:
|
||
g_list_free (keys);
|
||
|
||
return ret;
|
||
}
|
||
|
||
/**
|
||
* g_dbus_is_supported_address:
|
||
* @string: A string.
|
||
* @error: Return location for error or %NULL.
|
||
*
|
||
* Like g_dbus_is_address() but also checks if the library suppors the
|
||
* transports in @string and that key/value pairs for each transport
|
||
* are valid.
|
||
*
|
||
* Returns: %TRUE if @string is a valid D-Bus address that is
|
||
* supported by this library, %FALSE if @error is set.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
gboolean
|
||
g_dbus_is_supported_address (const gchar *string,
|
||
GError **error)
|
||
{
|
||
guint n;
|
||
gchar **a;
|
||
gboolean ret;
|
||
|
||
ret = FALSE;
|
||
|
||
g_return_val_if_fail (string != NULL, FALSE);
|
||
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
||
|
||
a = g_strsplit (string, ";", 0);
|
||
for (n = 0; a[n] != NULL; n++)
|
||
{
|
||
gchar *transport_name;
|
||
GHashTable *key_value_pairs;
|
||
gboolean supported;
|
||
|
||
if (!_g_dbus_address_parse_entry (a[n],
|
||
&transport_name,
|
||
&key_value_pairs,
|
||
error))
|
||
goto out;
|
||
|
||
supported = FALSE;
|
||
if (g_strcmp0 (transport_name, "unix") == 0)
|
||
supported = is_valid_unix (a[n], key_value_pairs, error);
|
||
else if (g_strcmp0 (transport_name, "tcp") == 0)
|
||
supported = is_valid_tcp (a[n], key_value_pairs, error);
|
||
else if (g_strcmp0 (transport_name, "nonce-tcp") == 0)
|
||
supported = is_valid_nonce_tcp (a[n], key_value_pairs, error);
|
||
else if (g_strcmp0 (a[n], "autolaunch:") == 0)
|
||
supported = TRUE;
|
||
|
||
g_free (transport_name);
|
||
g_hash_table_unref (key_value_pairs);
|
||
|
||
if (!supported)
|
||
goto out;
|
||
}
|
||
|
||
ret = TRUE;
|
||
|
||
out:
|
||
g_strfreev (a);
|
||
|
||
g_assert (ret || (!ret && (error == NULL || *error != NULL)));
|
||
|
||
return ret;
|
||
}
|
||
|
||
gboolean
|
||
_g_dbus_address_parse_entry (const gchar *address_entry,
|
||
gchar **out_transport_name,
|
||
GHashTable **out_key_value_pairs,
|
||
GError **error)
|
||
{
|
||
gboolean ret;
|
||
GHashTable *key_value_pairs;
|
||
gchar *transport_name;
|
||
gchar **kv_pairs;
|
||
const gchar *s;
|
||
guint n;
|
||
|
||
ret = FALSE;
|
||
kv_pairs = NULL;
|
||
transport_name = NULL;
|
||
key_value_pairs = NULL;
|
||
|
||
s = strchr (address_entry, ':');
|
||
if (s == NULL)
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Address element '%s' does not contain a colon (:)"),
|
||
address_entry);
|
||
goto out;
|
||
}
|
||
|
||
transport_name = g_strndup (address_entry, s - address_entry);
|
||
key_value_pairs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
|
||
|
||
kv_pairs = g_strsplit (s + 1, ",", 0);
|
||
for (n = 0; kv_pairs != NULL && kv_pairs[n] != NULL; n++)
|
||
{
|
||
const gchar *kv_pair = kv_pairs[n];
|
||
gchar *key;
|
||
gchar *value;
|
||
|
||
s = strchr (kv_pair, '=');
|
||
if (s == NULL)
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Key/Value pair %d, '%s', in address element '%s' does not contain an equal sign"),
|
||
n,
|
||
kv_pair,
|
||
address_entry);
|
||
goto out;
|
||
}
|
||
|
||
key = g_uri_unescape_segment (kv_pair, s, NULL);
|
||
value = g_uri_unescape_segment (s + 1, kv_pair + strlen (kv_pair), NULL);
|
||
if (key == NULL || value == NULL)
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Error unescaping key or value in Key/Value pair %d, '%s', in address element '%s'"),
|
||
n,
|
||
kv_pair,
|
||
address_entry);
|
||
g_free (key);
|
||
g_free (value);
|
||
goto out;
|
||
}
|
||
g_hash_table_insert (key_value_pairs, key, value);
|
||
}
|
||
|
||
ret = TRUE;
|
||
|
||
out:
|
||
g_strfreev (kv_pairs);
|
||
if (ret)
|
||
{
|
||
if (out_transport_name != NULL)
|
||
*out_transport_name = transport_name;
|
||
else
|
||
g_free (transport_name);
|
||
if (out_key_value_pairs != NULL)
|
||
*out_key_value_pairs = key_value_pairs;
|
||
else if (key_value_pairs != NULL)
|
||
g_hash_table_unref (key_value_pairs);
|
||
}
|
||
else
|
||
{
|
||
g_free (transport_name);
|
||
if (key_value_pairs != NULL)
|
||
g_hash_table_unref (key_value_pairs);
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
static GIOStream *
|
||
g_dbus_address_try_connect_one (const gchar *address_entry,
|
||
gchar **out_guid,
|
||
GCancellable *cancellable,
|
||
GError **error);
|
||
|
||
/* TODO: Declare an extension point called GDBusTransport (or similar)
|
||
* and move code below to extensions implementing said extension
|
||
* point. That way we can implement a D-Bus transport over X11 without
|
||
* making libgio link to libX11...
|
||
*/
|
||
static GIOStream *
|
||
g_dbus_address_connect (const gchar *address_entry,
|
||
const gchar *transport_name,
|
||
GHashTable *key_value_pairs,
|
||
GCancellable *cancellable,
|
||
GError **error)
|
||
{
|
||
GIOStream *ret;
|
||
GSocketConnectable *connectable;
|
||
const gchar *nonce_file;
|
||
|
||
connectable = NULL;
|
||
ret = NULL;
|
||
nonce_file = NULL;
|
||
|
||
if (FALSE)
|
||
{
|
||
}
|
||
#ifdef G_OS_UNIX
|
||
else if (g_strcmp0 (transport_name, "unix") == 0)
|
||
{
|
||
const gchar *path;
|
||
const gchar *abstract;
|
||
path = g_hash_table_lookup (key_value_pairs, "path");
|
||
abstract = g_hash_table_lookup (key_value_pairs, "abstract");
|
||
if ((path == NULL && abstract == NULL) || (path != NULL && abstract != NULL))
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Error in address '%s' - the unix transport requires exactly one of the "
|
||
"keys 'path' or 'abstract' to be set"),
|
||
address_entry);
|
||
}
|
||
else if (path != NULL)
|
||
{
|
||
connectable = G_SOCKET_CONNECTABLE (g_unix_socket_address_new (path));
|
||
}
|
||
else if (abstract != NULL)
|
||
{
|
||
connectable = G_SOCKET_CONNECTABLE (g_unix_socket_address_new_with_type (abstract,
|
||
-1,
|
||
G_UNIX_SOCKET_ADDRESS_ABSTRACT));
|
||
}
|
||
else
|
||
{
|
||
g_assert_not_reached ();
|
||
}
|
||
}
|
||
#endif
|
||
else if (g_strcmp0 (transport_name, "tcp") == 0 || g_strcmp0 (transport_name, "nonce-tcp") == 0)
|
||
{
|
||
const gchar *s;
|
||
const gchar *host;
|
||
glong port;
|
||
gchar *endp;
|
||
gboolean is_nonce;
|
||
|
||
is_nonce = (g_strcmp0 (transport_name, "nonce-tcp") == 0);
|
||
|
||
host = g_hash_table_lookup (key_value_pairs, "host");
|
||
if (host == NULL)
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Error in address '%s' - the host attribute is missing or malformed"),
|
||
address_entry);
|
||
goto out;
|
||
}
|
||
|
||
s = g_hash_table_lookup (key_value_pairs, "port");
|
||
if (s == NULL)
|
||
s = "0";
|
||
port = strtol (s, &endp, 10);
|
||
if ((*s == '\0' || *endp != '\0') || port < 0 || port >= 65536)
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Error in address '%s' - the port attribute is missing or malformed"),
|
||
address_entry);
|
||
goto out;
|
||
}
|
||
|
||
|
||
if (is_nonce)
|
||
{
|
||
nonce_file = g_hash_table_lookup (key_value_pairs, "noncefile");
|
||
if (nonce_file == NULL)
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Error in address '%s' - the noncefile attribute is missing or malformed"),
|
||
address_entry);
|
||
goto out;
|
||
}
|
||
}
|
||
|
||
/* TODO: deal with family key/value-pair */
|
||
connectable = g_network_address_new (host, port);
|
||
}
|
||
else if (g_strcmp0 (address_entry, "autolaunch:") == 0)
|
||
{
|
||
gchar *autolaunch_address;
|
||
autolaunch_address = get_session_address_dbus_launch (error);
|
||
if (autolaunch_address != NULL)
|
||
{
|
||
ret = g_dbus_address_try_connect_one (autolaunch_address, NULL, cancellable, error);
|
||
g_free (autolaunch_address);
|
||
goto out;
|
||
}
|
||
else
|
||
{
|
||
g_prefix_error (error, _("Error auto-launching: "));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Unknown or unsupported transport '%s' for address '%s'"),
|
||
transport_name,
|
||
address_entry);
|
||
}
|
||
|
||
if (connectable != NULL)
|
||
{
|
||
GSocketClient *client;
|
||
GSocketConnection *connection;
|
||
|
||
g_assert (ret == NULL);
|
||
client = g_socket_client_new ();
|
||
connection = g_socket_client_connect (client,
|
||
connectable,
|
||
cancellable,
|
||
error);
|
||
g_object_unref (connectable);
|
||
g_object_unref (client);
|
||
if (connection == NULL)
|
||
goto out;
|
||
|
||
ret = G_IO_STREAM (connection);
|
||
|
||
if (nonce_file != NULL)
|
||
{
|
||
gchar nonce_contents[16 + 1];
|
||
size_t num_bytes_read;
|
||
FILE *f;
|
||
|
||
/* be careful to read only 16 bytes - we also check that the file is only 16 bytes long */
|
||
f = fopen (nonce_file, "rb");
|
||
if (f == NULL)
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Error opening nonce file '%s': %s"),
|
||
nonce_file,
|
||
g_strerror (errno));
|
||
g_object_unref (ret);
|
||
ret = NULL;
|
||
goto out;
|
||
}
|
||
num_bytes_read = fread (nonce_contents,
|
||
sizeof (gchar),
|
||
16 + 1,
|
||
f);
|
||
if (num_bytes_read != 16)
|
||
{
|
||
if (num_bytes_read == 0)
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Error reading from nonce file '%s': %s"),
|
||
nonce_file,
|
||
g_strerror (errno));
|
||
}
|
||
else
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("Error reading from nonce file '%s', expected 16 bytes, got %d"),
|
||
nonce_file,
|
||
(gint) num_bytes_read);
|
||
}
|
||
g_object_unref (ret);
|
||
ret = NULL;
|
||
fclose (f);
|
||
goto out;
|
||
}
|
||
fclose (f);
|
||
|
||
if (!g_output_stream_write_all (g_io_stream_get_output_stream (ret),
|
||
nonce_contents,
|
||
16,
|
||
NULL,
|
||
cancellable,
|
||
error))
|
||
{
|
||
g_prefix_error (error, _("Error writing contents of nonce file '%s' to stream:"), nonce_file);
|
||
g_object_unref (ret);
|
||
ret = NULL;
|
||
goto out;
|
||
}
|
||
}
|
||
}
|
||
|
||
out:
|
||
|
||
return ret;
|
||
}
|
||
|
||
static GIOStream *
|
||
g_dbus_address_try_connect_one (const gchar *address_entry,
|
||
gchar **out_guid,
|
||
GCancellable *cancellable,
|
||
GError **error)
|
||
{
|
||
GIOStream *ret;
|
||
GHashTable *key_value_pairs;
|
||
gchar *transport_name;
|
||
const gchar *guid;
|
||
|
||
ret = NULL;
|
||
transport_name = NULL;
|
||
key_value_pairs = NULL;
|
||
|
||
if (!_g_dbus_address_parse_entry (address_entry,
|
||
&transport_name,
|
||
&key_value_pairs,
|
||
error))
|
||
goto out;
|
||
|
||
ret = g_dbus_address_connect (address_entry,
|
||
transport_name,
|
||
key_value_pairs,
|
||
cancellable,
|
||
error);
|
||
if (ret == NULL)
|
||
goto out;
|
||
|
||
guid = g_hash_table_lookup (key_value_pairs, "guid");
|
||
if (guid != NULL && out_guid != NULL)
|
||
*out_guid = g_strdup (guid);
|
||
|
||
out:
|
||
g_free (transport_name);
|
||
if (key_value_pairs != NULL)
|
||
g_hash_table_unref (key_value_pairs);
|
||
return ret;
|
||
}
|
||
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
typedef struct {
|
||
gchar *address;
|
||
gchar *guid;
|
||
} GetStreamData;
|
||
|
||
static void
|
||
get_stream_data_free (GetStreamData *data)
|
||
{
|
||
g_free (data->address);
|
||
g_free (data->guid);
|
||
g_free (data);
|
||
}
|
||
|
||
static void
|
||
get_stream_thread_func (GTask *task,
|
||
gpointer source_object,
|
||
gpointer task_data,
|
||
GCancellable *cancellable)
|
||
{
|
||
GetStreamData *data = task_data;
|
||
GIOStream *stream;
|
||
GError *error = NULL;
|
||
|
||
stream = g_dbus_address_get_stream_sync (data->address,
|
||
&data->guid,
|
||
cancellable,
|
||
&error);
|
||
if (stream)
|
||
g_task_return_pointer (task, stream, g_object_unref);
|
||
else
|
||
g_task_return_error (task, error);
|
||
}
|
||
|
||
/**
|
||
* g_dbus_address_get_stream:
|
||
* @address: A valid D-Bus address.
|
||
* @cancellable: (allow-none): A #GCancellable or %NULL.
|
||
* @callback: A #GAsyncReadyCallback to call when the request is satisfied.
|
||
* @user_data: Data to pass to @callback.
|
||
*
|
||
* Asynchronously connects to an endpoint specified by @address and
|
||
* sets up the connection so it is in a state to run the client-side
|
||
* of the D-Bus authentication conversation.
|
||
*
|
||
* When the operation is finished, @callback will be invoked. You can
|
||
* then call g_dbus_address_get_stream_finish() to get the result of
|
||
* the operation.
|
||
*
|
||
* This is an asynchronous failable function. See
|
||
* g_dbus_address_get_stream_sync() for the synchronous version.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
void
|
||
g_dbus_address_get_stream (const gchar *address,
|
||
GCancellable *cancellable,
|
||
GAsyncReadyCallback callback,
|
||
gpointer user_data)
|
||
{
|
||
GTask *task;
|
||
GetStreamData *data;
|
||
|
||
g_return_if_fail (address != NULL);
|
||
|
||
data = g_new0 (GetStreamData, 1);
|
||
data->address = g_strdup (address);
|
||
|
||
task = g_task_new (NULL, cancellable, callback, user_data);
|
||
g_task_set_source_tag (task, g_dbus_address_get_stream);
|
||
g_task_set_task_data (task, data, (GDestroyNotify) get_stream_data_free);
|
||
g_task_run_in_thread (task, get_stream_thread_func);
|
||
g_object_unref (task);
|
||
}
|
||
|
||
/**
|
||
* g_dbus_address_get_stream_finish:
|
||
* @res: A #GAsyncResult obtained from the GAsyncReadyCallback passed to g_dbus_address_get_stream().
|
||
* @out_guid: (optional) (out): %NULL or return location to store the GUID extracted from @address, if any.
|
||
* @error: Return location for error or %NULL.
|
||
*
|
||
* Finishes an operation started with g_dbus_address_get_stream().
|
||
*
|
||
* Returns: (transfer full): A #GIOStream or %NULL if @error is set.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
GIOStream *
|
||
g_dbus_address_get_stream_finish (GAsyncResult *res,
|
||
gchar **out_guid,
|
||
GError **error)
|
||
{
|
||
GTask *task;
|
||
GetStreamData *data;
|
||
GIOStream *ret;
|
||
|
||
g_return_val_if_fail (g_task_is_valid (res, NULL), NULL);
|
||
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
||
|
||
task = G_TASK (res);
|
||
ret = g_task_propagate_pointer (task, error);
|
||
|
||
if (ret != NULL && out_guid != NULL)
|
||
{
|
||
data = g_task_get_task_data (task);
|
||
*out_guid = data->guid;
|
||
data->guid = NULL;
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
/**
|
||
* g_dbus_address_get_stream_sync:
|
||
* @address: A valid D-Bus address.
|
||
* @out_guid: (optional) (out): %NULL or return location to store the GUID extracted from @address, if any.
|
||
* @cancellable: (allow-none): A #GCancellable or %NULL.
|
||
* @error: Return location for error or %NULL.
|
||
*
|
||
* Synchronously connects to an endpoint specified by @address and
|
||
* sets up the connection so it is in a state to run the client-side
|
||
* of the D-Bus authentication conversation.
|
||
*
|
||
* This is a synchronous failable function. See
|
||
* g_dbus_address_get_stream() for the asynchronous version.
|
||
*
|
||
* Returns: (transfer full): A #GIOStream or %NULL if @error is set.
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
GIOStream *
|
||
g_dbus_address_get_stream_sync (const gchar *address,
|
||
gchar **out_guid,
|
||
GCancellable *cancellable,
|
||
GError **error)
|
||
{
|
||
GIOStream *ret;
|
||
gchar **addr_array;
|
||
guint n;
|
||
GError *last_error;
|
||
|
||
g_return_val_if_fail (address != NULL, NULL);
|
||
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
||
|
||
ret = NULL;
|
||
last_error = NULL;
|
||
|
||
addr_array = g_strsplit (address, ";", 0);
|
||
if (addr_array != NULL && addr_array[0] == NULL)
|
||
{
|
||
last_error = g_error_new_literal (G_IO_ERROR,
|
||
G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("The given address is empty"));
|
||
goto out;
|
||
}
|
||
|
||
for (n = 0; addr_array != NULL && addr_array[n] != NULL; n++)
|
||
{
|
||
const gchar *addr = addr_array[n];
|
||
GError *this_error;
|
||
|
||
this_error = NULL;
|
||
ret = g_dbus_address_try_connect_one (addr,
|
||
out_guid,
|
||
cancellable,
|
||
&this_error);
|
||
if (ret != NULL)
|
||
{
|
||
goto out;
|
||
}
|
||
else
|
||
{
|
||
g_assert (this_error != NULL);
|
||
if (last_error != NULL)
|
||
g_error_free (last_error);
|
||
last_error = this_error;
|
||
}
|
||
}
|
||
|
||
out:
|
||
if (ret != NULL)
|
||
{
|
||
if (last_error != NULL)
|
||
g_error_free (last_error);
|
||
}
|
||
else
|
||
{
|
||
g_assert (last_error != NULL);
|
||
g_propagate_error (error, last_error);
|
||
}
|
||
|
||
g_strfreev (addr_array);
|
||
return ret;
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
/*
|
||
* Return the address of XDG_RUNTIME_DIR/bus if it exists, belongs to
|
||
* us, and is a socket, and we are on Unix.
|
||
*/
|
||
static gchar *
|
||
get_session_address_xdg (void)
|
||
{
|
||
#ifdef G_OS_UNIX
|
||
gchar *ret = NULL;
|
||
gchar *bus;
|
||
gchar *tmp;
|
||
GStatBuf buf;
|
||
|
||
bus = g_build_filename (g_get_user_runtime_dir (), "bus", NULL);
|
||
|
||
/* if ENOENT, EPERM, etc., quietly don't use it */
|
||
if (g_stat (bus, &buf) < 0)
|
||
goto out;
|
||
|
||
/* if it isn't ours, we have incorrectly inherited someone else's
|
||
* XDG_RUNTIME_DIR; silently don't use it
|
||
*/
|
||
if (buf.st_uid != geteuid ())
|
||
goto out;
|
||
|
||
/* if it isn't a socket, silently don't use it */
|
||
if ((buf.st_mode & S_IFMT) != S_IFSOCK)
|
||
goto out;
|
||
|
||
tmp = g_dbus_address_escape_value (bus);
|
||
ret = g_strconcat ("unix:path=", tmp, NULL);
|
||
g_free (tmp);
|
||
|
||
out:
|
||
g_free (bus);
|
||
return ret;
|
||
#else
|
||
return NULL;
|
||
#endif
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
#ifdef G_OS_UNIX
|
||
static gchar *
|
||
get_session_address_dbus_launch (GError **error)
|
||
{
|
||
gchar *ret;
|
||
gchar *machine_id;
|
||
gchar *command_line;
|
||
gchar *launch_stdout;
|
||
gchar *launch_stderr;
|
||
gint exit_status;
|
||
gchar *old_dbus_verbose;
|
||
gboolean restore_dbus_verbose;
|
||
|
||
ret = NULL;
|
||
machine_id = NULL;
|
||
command_line = NULL;
|
||
launch_stdout = NULL;
|
||
launch_stderr = NULL;
|
||
restore_dbus_verbose = FALSE;
|
||
old_dbus_verbose = NULL;
|
||
|
||
/* Don't run binaries as root if we're setuid. */
|
||
if (GLIB_PRIVATE_CALL (g_check_setuid) ())
|
||
{
|
||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||
_("Cannot spawn a message bus when setuid"));
|
||
goto out;
|
||
}
|
||
|
||
machine_id = _g_dbus_get_machine_id (error);
|
||
if (machine_id == NULL)
|
||
{
|
||
g_prefix_error (error, _("Cannot spawn a message bus without a machine-id: "));
|
||
goto out;
|
||
}
|
||
|
||
if (g_getenv ("DISPLAY") == NULL)
|
||
{
|
||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||
_("Cannot autolaunch D-Bus without X11 $DISPLAY"));
|
||
goto out;
|
||
}
|
||
|
||
/* We're using private libdbus facilities here. When everything
|
||
* (X11, Mac OS X, Windows) is spec'ed out correctly (not even the
|
||
* X11 property is correctly documented right now) we should
|
||
* consider using the spec instead of dbus-launch.
|
||
*
|
||
* --autolaunch=MACHINEID
|
||
* This option implies that dbus-launch should scan for a previ‐
|
||
* ously-started session and reuse the values found there. If no
|
||
* session is found, it will start a new session. The --exit-with-
|
||
* session option is implied if --autolaunch is given. This option
|
||
* is for the exclusive use of libdbus, you do not want to use it
|
||
* manually. It may change in the future.
|
||
*/
|
||
|
||
/* TODO: maybe provide a variable for where to look for the dbus-launch binary? */
|
||
command_line = g_strdup_printf ("dbus-launch --autolaunch=%s --binary-syntax --close-stderr", machine_id);
|
||
|
||
if (G_UNLIKELY (_g_dbus_debug_address ()))
|
||
{
|
||
_g_dbus_debug_print_lock ();
|
||
g_print ("GDBus-debug:Address: Running '%s' to get bus address (possibly autolaunching)\n", command_line);
|
||
old_dbus_verbose = g_strdup (g_getenv ("DBUS_VERBOSE"));
|
||
restore_dbus_verbose = TRUE;
|
||
g_setenv ("DBUS_VERBOSE", "1", TRUE);
|
||
_g_dbus_debug_print_unlock ();
|
||
}
|
||
|
||
if (!g_spawn_command_line_sync (command_line,
|
||
&launch_stdout,
|
||
&launch_stderr,
|
||
&exit_status,
|
||
error))
|
||
{
|
||
goto out;
|
||
}
|
||
|
||
if (!g_spawn_check_exit_status (exit_status, error))
|
||
{
|
||
g_prefix_error (error, _("Error spawning command line '%s': "), command_line);
|
||
goto out;
|
||
}
|
||
|
||
/* From the dbus-launch(1) man page:
|
||
*
|
||
* --binary-syntax Write to stdout a nul-terminated bus address,
|
||
* then the bus PID as a binary integer of size sizeof(pid_t),
|
||
* then the bus X window ID as a binary integer of size
|
||
* sizeof(long). Integers are in the machine's byte order, not
|
||
* network byte order or any other canonical byte order.
|
||
*/
|
||
ret = g_strdup (launch_stdout);
|
||
|
||
out:
|
||
if (G_UNLIKELY (_g_dbus_debug_address ()))
|
||
{
|
||
gchar *s;
|
||
_g_dbus_debug_print_lock ();
|
||
g_print ("GDBus-debug:Address: dbus-launch output:");
|
||
if (launch_stdout != NULL)
|
||
{
|
||
s = _g_dbus_hexdump (launch_stdout, strlen (launch_stdout) + 1 + sizeof (pid_t) + sizeof (long), 2);
|
||
g_print ("\n%s", s);
|
||
g_free (s);
|
||
}
|
||
else
|
||
{
|
||
g_print (" (none)\n");
|
||
}
|
||
g_print ("GDBus-debug:Address: dbus-launch stderr output:");
|
||
if (launch_stderr != NULL)
|
||
g_print ("\n%s", launch_stderr);
|
||
else
|
||
g_print (" (none)\n");
|
||
_g_dbus_debug_print_unlock ();
|
||
}
|
||
|
||
g_free (machine_id);
|
||
g_free (command_line);
|
||
g_free (launch_stdout);
|
||
g_free (launch_stderr);
|
||
if (G_UNLIKELY (restore_dbus_verbose))
|
||
{
|
||
if (old_dbus_verbose != NULL)
|
||
g_setenv ("DBUS_VERBOSE", old_dbus_verbose, TRUE);
|
||
else
|
||
g_unsetenv ("DBUS_VERBOSE");
|
||
}
|
||
g_free (old_dbus_verbose);
|
||
return ret;
|
||
}
|
||
|
||
/* end of G_OS_UNIX case */
|
||
#elif defined(G_OS_WIN32)
|
||
|
||
#define DBUS_DAEMON_ADDRESS_INFO "DBusDaemonAddressInfo"
|
||
#define DBUS_DAEMON_MUTEX "DBusDaemonMutex"
|
||
#define UNIQUE_DBUS_INIT_MUTEX "UniqueDBusInitMutex"
|
||
#define DBUS_AUTOLAUNCH_MUTEX "DBusAutolaunchMutex"
|
||
|
||
static void
|
||
release_mutex (HANDLE mutex)
|
||
{
|
||
ReleaseMutex (mutex);
|
||
CloseHandle (mutex);
|
||
}
|
||
|
||
static HANDLE
|
||
acquire_mutex (const char *mutexname)
|
||
{
|
||
HANDLE mutex;
|
||
DWORD res;
|
||
|
||
mutex = CreateMutexA (NULL, FALSE, mutexname);
|
||
if (!mutex)
|
||
return 0;
|
||
|
||
res = WaitForSingleObject (mutex, INFINITE);
|
||
switch (res)
|
||
{
|
||
case WAIT_ABANDONED:
|
||
release_mutex (mutex);
|
||
return 0;
|
||
case WAIT_FAILED:
|
||
case WAIT_TIMEOUT:
|
||
return 0;
|
||
}
|
||
|
||
return mutex;
|
||
}
|
||
|
||
static gboolean
|
||
is_mutex_owned (const char *mutexname)
|
||
{
|
||
HANDLE mutex;
|
||
gboolean res = FALSE;
|
||
|
||
mutex = CreateMutexA (NULL, FALSE, mutexname);
|
||
if (WaitForSingleObject (mutex, 10) == WAIT_TIMEOUT)
|
||
res = TRUE;
|
||
else
|
||
ReleaseMutex (mutex);
|
||
CloseHandle (mutex);
|
||
|
||
return res;
|
||
}
|
||
|
||
static char *
|
||
read_shm (const char *shm_name)
|
||
{
|
||
HANDLE shared_mem;
|
||
char *shared_data;
|
||
char *res;
|
||
int i;
|
||
|
||
res = NULL;
|
||
|
||
for (i = 0; i < 20; i++)
|
||
{
|
||
shared_mem = OpenFileMappingA (FILE_MAP_READ, FALSE, shm_name);
|
||
if (shared_mem != 0)
|
||
break;
|
||
Sleep (100);
|
||
}
|
||
|
||
if (shared_mem != 0)
|
||
{
|
||
shared_data = MapViewOfFile (shared_mem, FILE_MAP_READ, 0, 0, 0);
|
||
if (shared_data != NULL)
|
||
{
|
||
res = g_strdup (shared_data);
|
||
UnmapViewOfFile (shared_data);
|
||
}
|
||
CloseHandle (shared_mem);
|
||
}
|
||
|
||
return res;
|
||
}
|
||
|
||
static HANDLE
|
||
set_shm (const char *shm_name, const char *value)
|
||
{
|
||
HANDLE shared_mem;
|
||
char *shared_data;
|
||
|
||
shared_mem = CreateFileMappingA (INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
|
||
0, strlen (value) + 1, shm_name);
|
||
if (shared_mem == 0)
|
||
return 0;
|
||
|
||
shared_data = MapViewOfFile (shared_mem, FILE_MAP_WRITE, 0, 0, 0 );
|
||
if (shared_data == NULL)
|
||
return 0;
|
||
|
||
strcpy (shared_data, value);
|
||
|
||
UnmapViewOfFile (shared_data);
|
||
|
||
return shared_mem;
|
||
}
|
||
|
||
/* These keep state between publish_session_bus and unpublish_session_bus */
|
||
static HANDLE published_daemon_mutex;
|
||
static HANDLE published_shared_mem;
|
||
|
||
static gboolean
|
||
publish_session_bus (const char *address)
|
||
{
|
||
HANDLE init_mutex;
|
||
|
||
init_mutex = acquire_mutex (UNIQUE_DBUS_INIT_MUTEX);
|
||
|
||
published_daemon_mutex = CreateMutexA (NULL, FALSE, DBUS_DAEMON_MUTEX);
|
||
if (WaitForSingleObject (published_daemon_mutex, 10 ) != WAIT_OBJECT_0)
|
||
{
|
||
release_mutex (init_mutex);
|
||
CloseHandle (published_daemon_mutex);
|
||
published_daemon_mutex = NULL;
|
||
return FALSE;
|
||
}
|
||
|
||
published_shared_mem = set_shm (DBUS_DAEMON_ADDRESS_INFO, address);
|
||
if (!published_shared_mem)
|
||
{
|
||
release_mutex (init_mutex);
|
||
CloseHandle (published_daemon_mutex);
|
||
published_daemon_mutex = NULL;
|
||
return FALSE;
|
||
}
|
||
|
||
release_mutex (init_mutex);
|
||
return TRUE;
|
||
}
|
||
|
||
static void
|
||
unpublish_session_bus (void)
|
||
{
|
||
HANDLE init_mutex;
|
||
|
||
init_mutex = acquire_mutex (UNIQUE_DBUS_INIT_MUTEX);
|
||
|
||
CloseHandle (published_shared_mem);
|
||
published_shared_mem = NULL;
|
||
|
||
release_mutex (published_daemon_mutex);
|
||
published_daemon_mutex = NULL;
|
||
|
||
release_mutex (init_mutex);
|
||
}
|
||
|
||
static void
|
||
wait_console_window (void)
|
||
{
|
||
FILE *console = fopen ("CONOUT$", "w");
|
||
|
||
SetConsoleTitleW (L"gdbus-daemon output. Type any character to close this window.");
|
||
fprintf (console, _("(Type any character to close this window)\n"));
|
||
fflush (console);
|
||
_getch ();
|
||
}
|
||
|
||
static void
|
||
open_console_window (void)
|
||
{
|
||
if (((HANDLE) _get_osfhandle (fileno (stdout)) == INVALID_HANDLE_VALUE ||
|
||
(HANDLE) _get_osfhandle (fileno (stderr)) == INVALID_HANDLE_VALUE) && AllocConsole ())
|
||
{
|
||
if ((HANDLE) _get_osfhandle (fileno (stdout)) == INVALID_HANDLE_VALUE)
|
||
freopen ("CONOUT$", "w", stdout);
|
||
|
||
if ((HANDLE) _get_osfhandle (fileno (stderr)) == INVALID_HANDLE_VALUE)
|
||
freopen ("CONOUT$", "w", stderr);
|
||
|
||
SetConsoleTitleW (L"gdbus-daemon debug output.");
|
||
|
||
atexit (wait_console_window);
|
||
}
|
||
}
|
||
static void
|
||
idle_timeout_cb (GDBusDaemon *daemon, gpointer user_data)
|
||
{
|
||
GMainLoop *loop = user_data;
|
||
g_main_loop_quit (loop);
|
||
}
|
||
|
||
/* Satisfies STARTF_FORCEONFEEDBACK */
|
||
static void
|
||
turn_off_the_starting_cursor (void)
|
||
{
|
||
MSG msg;
|
||
BOOL bRet;
|
||
|
||
PostQuitMessage (0);
|
||
|
||
while ((bRet = GetMessage (&msg, 0, 0, 0)) != 0)
|
||
{
|
||
if (bRet == -1)
|
||
continue;
|
||
|
||
TranslateMessage (&msg);
|
||
DispatchMessage (&msg);
|
||
}
|
||
}
|
||
|
||
__declspec(dllexport) void CALLBACK g_win32_run_session_bus (HWND hwnd, HINSTANCE hinst, char *cmdline, int nCmdShow);
|
||
|
||
__declspec(dllexport) void CALLBACK
|
||
g_win32_run_session_bus (HWND hwnd, HINSTANCE hinst, char *cmdline, int nCmdShow)
|
||
{
|
||
GDBusDaemon *daemon;
|
||
GMainLoop *loop;
|
||
const char *address;
|
||
GError *error = NULL;
|
||
|
||
turn_off_the_starting_cursor ();
|
||
|
||
if (g_getenv ("GDBUS_DAEMON_DEBUG") != NULL)
|
||
open_console_window ();
|
||
|
||
loop = g_main_loop_new (NULL, FALSE);
|
||
|
||
address = "nonce-tcp:";
|
||
daemon = _g_dbus_daemon_new (address, NULL, &error);
|
||
if (daemon == NULL)
|
||
{
|
||
g_printerr ("Can't init bus: %s\n", error->message);
|
||
return;
|
||
}
|
||
|
||
g_signal_connect (daemon, "idle-timeout", G_CALLBACK (idle_timeout_cb), loop);
|
||
|
||
if ( publish_session_bus (_g_dbus_daemon_get_address (daemon)))
|
||
{
|
||
g_main_loop_run (loop);
|
||
|
||
unpublish_session_bus ();
|
||
}
|
||
|
||
g_main_loop_unref (loop);
|
||
g_object_unref (daemon);
|
||
}
|
||
|
||
static gchar *
|
||
get_session_address_dbus_launch (GError **error)
|
||
{
|
||
HANDLE autolaunch_mutex, init_mutex;
|
||
char *address = NULL;
|
||
wchar_t gio_path[MAX_PATH+1+200];
|
||
|
||
autolaunch_mutex = acquire_mutex (DBUS_AUTOLAUNCH_MUTEX);
|
||
|
||
init_mutex = acquire_mutex (UNIQUE_DBUS_INIT_MUTEX);
|
||
|
||
if (is_mutex_owned (DBUS_DAEMON_MUTEX))
|
||
address = read_shm (DBUS_DAEMON_ADDRESS_INFO);
|
||
|
||
release_mutex (init_mutex);
|
||
|
||
if (address == NULL)
|
||
{
|
||
gio_path[MAX_PATH] = 0;
|
||
if (GetModuleFileNameW (_g_io_win32_get_module (), gio_path, MAX_PATH))
|
||
{
|
||
PROCESS_INFORMATION pi = { 0 };
|
||
STARTUPINFOW si = { 0 };
|
||
BOOL res;
|
||
wchar_t gio_path_short[MAX_PATH];
|
||
wchar_t rundll_path[MAX_PATH*2];
|
||
wchar_t args[MAX_PATH*4];
|
||
|
||
GetShortPathNameW (gio_path, gio_path_short, MAX_PATH);
|
||
|
||
GetWindowsDirectoryW (rundll_path, MAX_PATH);
|
||
wcscat (rundll_path, L"\\rundll32.exe");
|
||
if (GetFileAttributesW (rundll_path) == INVALID_FILE_ATTRIBUTES)
|
||
{
|
||
GetSystemDirectoryW (rundll_path, MAX_PATH);
|
||
wcscat (rundll_path, L"\\rundll32.exe");
|
||
}
|
||
|
||
wcscpy (args, L"\"");
|
||
wcscat (args, rundll_path);
|
||
wcscat (args, L"\" ");
|
||
wcscat (args, gio_path_short);
|
||
#if defined(_WIN64) || defined(_M_X64) || defined(_M_AMD64)
|
||
wcscat (args, L",g_win32_run_session_bus");
|
||
#elif defined (_MSC_VER)
|
||
wcscat (args, L",_g_win32_run_session_bus@16");
|
||
#else
|
||
wcscat (args, L",g_win32_run_session_bus@16");
|
||
#endif
|
||
|
||
res = CreateProcessW (rundll_path, args,
|
||
0, 0, FALSE,
|
||
NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW | DETACHED_PROCESS,
|
||
0, NULL /* TODO: Should be root */,
|
||
&si, &pi);
|
||
if (res)
|
||
address = read_shm (DBUS_DAEMON_ADDRESS_INFO);
|
||
}
|
||
}
|
||
|
||
release_mutex (autolaunch_mutex);
|
||
|
||
if (address == NULL)
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_FAILED,
|
||
_("Session dbus not running, and autolaunch failed"));
|
||
|
||
return address;
|
||
}
|
||
#else /* neither G_OS_UNIX nor G_OS_WIN32 */
|
||
static gchar *
|
||
get_session_address_dbus_launch (GError **error)
|
||
{
|
||
g_set_error (error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_FAILED,
|
||
_("Cannot determine session bus address (not implemented for this OS)"));
|
||
return NULL;
|
||
}
|
||
#endif /* neither G_OS_UNIX nor G_OS_WIN32 */
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
static gchar *
|
||
get_session_address_platform_specific (GError **error)
|
||
{
|
||
gchar *ret;
|
||
|
||
/* Use XDG_RUNTIME_DIR/bus if it exists and is suitable. This is appropriate
|
||
* for systems using the "a session is a user-session" model described in
|
||
* <http://lists.freedesktop.org/archives/dbus/2015-January/016522.html>,
|
||
* and implemented in dbus >= 1.9.14 and sd-bus.
|
||
*
|
||
* On systems following the more traditional "a session is a login-session"
|
||
* model, this will fail and we'll fall through to X11 autolaunching
|
||
* (dbus-launch) below.
|
||
*/
|
||
ret = get_session_address_xdg ();
|
||
|
||
if (ret != NULL)
|
||
return ret;
|
||
|
||
/* TODO (#694472): try launchd on OS X, like
|
||
* _dbus_lookup_session_address_launchd() does, since
|
||
* 'dbus-launch --autolaunch' probably won't work there
|
||
*/
|
||
|
||
/* As a last resort, try the "autolaunch:" transport. On Unix this means
|
||
* X11 autolaunching; on Windows this means a different autolaunching
|
||
* mechanism based on shared memory.
|
||
*/
|
||
return get_session_address_dbus_launch (error);
|
||
}
|
||
|
||
/* ---------------------------------------------------------------------------------------------------- */
|
||
|
||
/**
|
||
* g_dbus_address_get_for_bus_sync:
|
||
* @bus_type: a #GBusType
|
||
* @cancellable: (allow-none): a #GCancellable or %NULL
|
||
* @error: return location for error or %NULL
|
||
*
|
||
* Synchronously looks up the D-Bus address for the well-known message
|
||
* bus instance specified by @bus_type. This may involve using various
|
||
* platform specific mechanisms.
|
||
*
|
||
* Returns: a valid D-Bus address string for @bus_type or %NULL if
|
||
* @error is set
|
||
*
|
||
* Since: 2.26
|
||
*/
|
||
gchar *
|
||
g_dbus_address_get_for_bus_sync (GBusType bus_type,
|
||
GCancellable *cancellable,
|
||
GError **error)
|
||
{
|
||
gchar *ret, *s = NULL;
|
||
const gchar *starter_bus;
|
||
GError *local_error;
|
||
|
||
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
||
|
||
ret = NULL;
|
||
local_error = NULL;
|
||
|
||
if (G_UNLIKELY (_g_dbus_debug_address ()))
|
||
{
|
||
guint n;
|
||
_g_dbus_debug_print_lock ();
|
||
s = _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type);
|
||
g_print ("GDBus-debug:Address: In g_dbus_address_get_for_bus_sync() for bus type '%s'\n",
|
||
s);
|
||
g_free (s);
|
||
for (n = 0; n < 3; n++)
|
||
{
|
||
const gchar *k;
|
||
const gchar *v;
|
||
switch (n)
|
||
{
|
||
case 0: k = "DBUS_SESSION_BUS_ADDRESS"; break;
|
||
case 1: k = "DBUS_SYSTEM_BUS_ADDRESS"; break;
|
||
case 2: k = "DBUS_STARTER_BUS_TYPE"; break;
|
||
default: g_assert_not_reached ();
|
||
}
|
||
v = g_getenv (k);
|
||
g_print ("GDBus-debug:Address: env var %s", k);
|
||
if (v != NULL)
|
||
g_print ("='%s'\n", v);
|
||
else
|
||
g_print (" is not set\n");
|
||
}
|
||
_g_dbus_debug_print_unlock ();
|
||
}
|
||
|
||
switch (bus_type)
|
||
{
|
||
case G_BUS_TYPE_SYSTEM:
|
||
ret = g_strdup (g_getenv ("DBUS_SYSTEM_BUS_ADDRESS"));
|
||
if (ret == NULL)
|
||
{
|
||
ret = g_strdup ("unix:path=/var/run/dbus/system_bus_socket");
|
||
}
|
||
break;
|
||
|
||
case G_BUS_TYPE_SESSION:
|
||
ret = g_strdup (g_getenv ("DBUS_SESSION_BUS_ADDRESS"));
|
||
if (ret == NULL)
|
||
{
|
||
ret = get_session_address_platform_specific (&local_error);
|
||
}
|
||
break;
|
||
|
||
case G_BUS_TYPE_STARTER:
|
||
starter_bus = g_getenv ("DBUS_STARTER_BUS_TYPE");
|
||
if (g_strcmp0 (starter_bus, "session") == 0)
|
||
{
|
||
ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SESSION, cancellable, &local_error);
|
||
goto out;
|
||
}
|
||
else if (g_strcmp0 (starter_bus, "system") == 0)
|
||
{
|
||
ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SYSTEM, cancellable, &local_error);
|
||
goto out;
|
||
}
|
||
else
|
||
{
|
||
if (starter_bus != NULL)
|
||
{
|
||
g_set_error (&local_error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_FAILED,
|
||
_("Cannot determine bus address from DBUS_STARTER_BUS_TYPE environment variable"
|
||
" - unknown value '%s'"),
|
||
starter_bus);
|
||
}
|
||
else
|
||
{
|
||
g_set_error_literal (&local_error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_FAILED,
|
||
_("Cannot determine bus address because the DBUS_STARTER_BUS_TYPE environment "
|
||
"variable is not set"));
|
||
}
|
||
}
|
||
break;
|
||
|
||
default:
|
||
g_set_error (&local_error,
|
||
G_IO_ERROR,
|
||
G_IO_ERROR_FAILED,
|
||
_("Unknown bus type %d"),
|
||
bus_type);
|
||
break;
|
||
}
|
||
|
||
out:
|
||
if (G_UNLIKELY (_g_dbus_debug_address ()))
|
||
{
|
||
_g_dbus_debug_print_lock ();
|
||
s = _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type);
|
||
if (ret != NULL)
|
||
{
|
||
g_print ("GDBus-debug:Address: Returning address '%s' for bus type '%s'\n",
|
||
ret, s);
|
||
}
|
||
else
|
||
{
|
||
g_print ("GDBus-debug:Address: Cannot look-up address bus type '%s': %s\n",
|
||
s, local_error ? local_error->message : "");
|
||
}
|
||
g_free (s);
|
||
_g_dbus_debug_print_unlock ();
|
||
}
|
||
|
||
if (local_error != NULL)
|
||
g_propagate_error (error, local_error);
|
||
|
||
return ret;
|
||
}
|
||
|
||
/**
|
||
* g_dbus_address_escape_value:
|
||
* @string: an unescaped string to be included in a D-Bus address
|
||
* as the value in a key-value pair
|
||
*
|
||
* Escape @string so it can appear in a D-Bus address as the value
|
||
* part of a key-value pair.
|
||
*
|
||
* For instance, if @string is "/run/bus-for-:0",
|
||
* this function would return "/run/bus-for-%3A0",
|
||
* which could be used in a D-Bus address like
|
||
* "unix:nonce-tcp:host=127.0.0.1,port=42,noncefile=/run/bus-for-%3A0".
|
||
*
|
||
* Returns: (transfer full): a copy of @string with all
|
||
* non-optionally-escaped bytes escaped
|
||
*
|
||
* Since: 2.36
|
||
*/
|
||
gchar *
|
||
g_dbus_address_escape_value (const gchar *string)
|
||
{
|
||
GString *s;
|
||
gsize i;
|
||
|
||
g_return_val_if_fail (string != NULL, NULL);
|
||
|
||
/* There will often not be anything needing escaping at all. */
|
||
s = g_string_sized_new (strlen (string));
|
||
|
||
/* D-Bus address escaping is mostly the same as URI escaping... */
|
||
g_string_append_uri_escaped (s, string, "\\/", FALSE);
|
||
|
||
/* ... but '~' is an unreserved character in URIs, but a
|
||
* non-optionally-escaped character in D-Bus addresses. */
|
||
for (i = 0; i < s->len; i++)
|
||
{
|
||
if (G_UNLIKELY (s->str[i] == '~'))
|
||
{
|
||
s->str[i] = '%';
|
||
g_string_insert (s, i + 1, "7E");
|
||
i += 2;
|
||
}
|
||
}
|
||
|
||
return g_string_free (s, FALSE);
|
||
}
|