GDBusProxy: Call into well-known name if no name owner currently exists

This is really what (API) users expect from GDBusProxy - in
particular, mclasen and I ran into this problem while debugging a
upower issue, see

 https://bugzilla.redhat.com/show_bug.cgi?id=624125

In a nutshell, the problem is that polkitd crashes while upower holds
a PolkitAuthority object (which in turns contains a GDBusProxy for the
well-known name org.freedesktop.PolicyKit1). This means that
subsequent calls on the PolkitAuthority (which is translated into
calls into the GDBusProxy) fails since :g-name-owner is NULL.

With this fix, we'll be requesting the bus daemon to launch polkitd
since we will start calling into org.freedesktop.PolicyKit1 as soon as
we notice that there is no owner for this name.

Unfortunately our test suite doesn't cover service activation so there
is no way to reliably test this. I will file a bug about this.

Signed-off-by: David Zeuthen <davidz@redhat.com>
This commit is contained in:
David Zeuthen 2010-08-18 11:35:25 -04:00
parent c2945808ac
commit 5bb94348f4

View File

@ -61,9 +61,13 @@
* name is tracked and can be read from
* #GDBusProxy:g-name-owner. Connect to the #GObject::notify signal to
* get notified of changes. Additionally, only signals and property
* changes emitted from the current name owner are considered. This
* avoids a number of race conditions when the name is lost by one
* owner and claimed by another.
* changes emitted from the current name owner are considered and
* calls are always sent to the current name owner. This avoids a
* number of race conditions when the name is lost by one owner and
* claimed by another. However, if no name owner currently exists,
* then calls will be sent to the well-known name which may result in
* the message bus launching an owner (unless
* %G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START is set).
*
* The generic #GDBusProxy::g-properties-changed and #GDBusProxy::g-signal
* signals are not very convenient to work with. Therefore, the recommended
@ -2179,6 +2183,31 @@ lookup_method_info_or_warn (GDBusProxy *proxy,
return info;
}
static const gchar *
get_destination_for_call (GDBusProxy *proxy)
{
const gchar *ret;
ret = NULL;
/* If proxy->priv->name is a unique name, then proxy->priv->name_owner
* is never NULL and always the same as proxy->priv->name. We use this
* knowledge to avoid checking if proxy->priv->name is a unique or
* well-known name.
*/
ret = proxy->priv->name_owner;
if (ret != NULL)
goto out;
if (proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START)
goto out;
ret = proxy->priv->name;
out:
return ret;
}
/**
* g_dbus_proxy_call:
* @proxy: A #GDBusProxy.
@ -2243,9 +2272,9 @@ g_dbus_proxy_call (GDBusProxy *proxy,
gboolean was_split;
gchar *split_interface_name;
const gchar *split_method_name;
const GDBusMethodInfo *expected_method_info;
const gchar *target_method_name;
const gchar *target_interface_name;
const gchar *destination;
GVariantType *reply_type;
g_return_if_fail (G_IS_DBUS_PROXY (proxy));
@ -2253,6 +2282,9 @@ g_dbus_proxy_call (GDBusProxy *proxy,
g_return_if_fail (parameters == NULL || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE));
g_return_if_fail (timeout_msec == -1 || timeout_msec >= 0);
reply_type = NULL;
split_interface_name = NULL;
simple = g_simple_async_result_new (G_OBJECT (proxy),
callback,
user_data,
@ -2265,17 +2297,30 @@ g_dbus_proxy_call (GDBusProxy *proxy,
g_object_set_data_full (G_OBJECT (simple), "-gdbus-proxy-method-name", g_strdup (target_method_name), g_free);
/* Warn if method is unexpected (cf. :g-interface-info) */
expected_method_info = NULL;
if (!was_split)
expected_method_info = lookup_method_info_or_warn (proxy, target_method_name);
{
const GDBusMethodInfo *expected_method_info;
expected_method_info = lookup_method_info_or_warn (proxy, target_method_name);
if (expected_method_info != NULL)
reply_type = _g_dbus_compute_complete_signature (expected_method_info->out_args);
}
if (expected_method_info)
reply_type = _g_dbus_compute_complete_signature (expected_method_info->out_args);
else
reply_type = NULL;
destination = NULL;
if (proxy->priv->name != NULL)
{
destination = get_destination_for_call (proxy);
if (destination == NULL)
{
g_simple_async_result_set_error (simple,
G_IO_ERROR,
G_IO_ERROR_FAILED,
_("Cannot invoke method; proxy is for a well-known name without an owner and proxy was constructed with the G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START flag"));
goto out;
}
}
g_dbus_connection_call (proxy->priv->connection,
proxy->priv->name_owner,
destination,
proxy->priv->object_path,
target_interface_name,
target_method_name,
@ -2287,6 +2332,7 @@ g_dbus_proxy_call (GDBusProxy *proxy,
(GAsyncReadyCallback) reply_cb,
simple);
out:
if (reply_type != NULL)
g_variant_type_free (reply_type);
@ -2392,9 +2438,9 @@ g_dbus_proxy_call_sync (GDBusProxy *proxy,
gboolean was_split;
gchar *split_interface_name;
const gchar *split_method_name;
const GDBusMethodInfo *expected_method_info;
const gchar *target_method_name;
const gchar *target_interface_name;
const gchar *destination;
GVariantType *reply_type;
g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL);
@ -2403,32 +2449,37 @@ g_dbus_proxy_call_sync (GDBusProxy *proxy,
g_return_val_if_fail (timeout_msec == -1 || timeout_msec >= 0, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
reply_type = NULL;
was_split = maybe_split_method_name (method_name, &split_interface_name, &split_method_name);
target_method_name = was_split ? split_method_name : method_name;
target_interface_name = was_split ? split_interface_name : proxy->priv->interface_name;
if (proxy->priv->expected_interface)
/* Warn if method is unexpected (cf. :g-interface-info) */
if (!was_split)
{
expected_method_info = g_dbus_interface_info_lookup_method (proxy->priv->expected_interface, target_method_name);
if (expected_method_info == NULL)
const GDBusMethodInfo *expected_method_info;
expected_method_info = lookup_method_info_or_warn (proxy, target_method_name);
if (expected_method_info != NULL)
reply_type = _g_dbus_compute_complete_signature (expected_method_info->out_args);
}
destination = NULL;
if (proxy->priv->name != NULL)
{
destination = get_destination_for_call (proxy);
if (destination == NULL)
{
g_warning ("Trying to invoke method `%s' which isn't in expected interface `%s'",
target_method_name,
target_interface_name);
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
_("Cannot invoke method; proxy is for a well-known name without an owner and proxy was constructed with the G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START flag"));
goto out;
}
}
else
{
expected_method_info = NULL;
}
if (expected_method_info)
reply_type = _g_dbus_compute_complete_signature (expected_method_info->out_args);
else
reply_type = NULL;
ret = g_dbus_connection_call_sync (proxy->priv->connection,
proxy->priv->name_owner,
destination,
proxy->priv->object_path,
target_interface_name,
target_method_name,
@ -2439,6 +2490,7 @@ g_dbus_proxy_call_sync (GDBusProxy *proxy,
cancellable,
error);
out:
if (reply_type != NULL)
g_variant_type_free (reply_type);