Skip to content

Commit

Permalink
app-info: Transition host apps to registered
Browse files Browse the repository at this point in the history
If a host app is created due an early D-Bus, then the registration
happens, transition it from "guessed" to registered. But only do it
once.

This allows smoother recovery of apps if the portal service disappears
for some reason, e.g. crashes or restarts.

Adjust the registry tests to exercise this code path.

Closes: #1612
  • Loading branch information
GeorgesStavracas committed Feb 7, 2025
1 parent 53cb9dd commit ea52415
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 13 deletions.
82 changes: 76 additions & 6 deletions src/xdp-app-info.c
Original file line number Diff line number Diff line change
Expand Up @@ -917,13 +917,20 @@ ensure_app_info_by_unique_name (void)
}

static void
cache_insert_app_info (const char *sender,
XdpAppInfo *app_info)
cache_insert_app_info_unlocked (const char *sender,
XdpAppInfo *app_info)
{
G_LOCK (app_infos);
ensure_app_info_by_unique_name ();
g_hash_table_insert (app_info_by_unique_name, g_strdup (sender),
g_object_ref (app_info));
}

static void
cache_insert_app_info (const char *sender,
XdpAppInfo *app_info)
{
G_LOCK (app_infos);
cache_insert_app_info_unlocked (sender, app_info);
G_UNLOCK (app_infos);
}

Expand Down Expand Up @@ -1105,6 +1112,61 @@ xdp_invocation_ensure_app_info_sync (GDBusMethodInvocation *invocation,
error);
}

static XdpAppInfo *
transition_host_app_info_to_registered (GDBusMethodInvocation *invocation,
const char *app_id,
GCancellable *cancellable,
GError **error)
{
GDBusConnection *connection = g_dbus_method_invocation_get_connection (invocation);
const char *sender = g_dbus_method_invocation_get_sender (invocation);
g_autoptr(GMutexLocker) app_infos_mutex_locker = NULL;
g_autoptr(XdpAppInfo) registered_app_info = NULL;
XdpAppInfo *app_info;

g_assert (app_info_by_unique_name != NULL);

app_infos_mutex_locker = g_mutex_locker_new (&G_LOCK_NAME (app_infos));

app_info = g_hash_table_lookup (app_info_by_unique_name, sender);
g_assert (XDP_IS_APP_INFO (app_info));

if (!xdp_app_info_is_host (app_info))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"App is not a host system app");
return NULL;
}

if (xdp_app_info_is_registered (app_info))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Connection already associated and registered with an application ID");
return NULL;
}

registered_app_info = maybe_create_registered_test_app_info (app_id);

if (!registered_app_info)
{
g_autofd int pidfd = -1;
uint32_t pid;

if (!xdp_connection_get_pidfd (connection, sender, cancellable, &pidfd, &pid, error))
return NULL;

registered_app_info = xdp_app_info_host_new_registered (pidfd, app_id, error);
if (!registered_app_info)
return NULL;
}

g_debug ("Replacing host app info with a registered one for '%s'", xdp_app_info_get_id (registered_app_info));

cache_insert_app_info_unlocked (sender, registered_app_info);

return g_steal_pointer (&registered_app_info);
}

XdpAppInfo *
xdp_invocation_register_host_app_info_sync (GDBusMethodInvocation *invocation,
const char *app_id,
Expand All @@ -1116,9 +1178,17 @@ xdp_invocation_register_host_app_info_sync (GDBusMethodInvocation *invocation,

if (cache_has_app_info_by_sender (sender))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Connection already associated with an application ID");
return NULL;
g_autoptr(XdpAppInfo) app_info = NULL;

app_info = transition_host_app_info_to_registered (invocation,
app_id,
cancellable,
error);

if (!app_info)
return NULL;

return g_steal_pointer (&app_info);
}

return xdp_connection_create_host_app_info_sync (connection,
Expand Down
13 changes: 6 additions & 7 deletions tests/test_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,17 +78,14 @@ def test_late_register(self, portals, dbus_con, app_id):
registry_intf = xdp.get_portal_iface(dbus_con, "Registry", domain="host")
mock_intf = xdp.get_mock_iface(dbus_con)

expected_app_id = app_id
unexpected_app_id = "org.example.CorrectAppId"

session = self.create_dummy_session(dbus_con)

expected_app_id = app_id
app_id = mock_intf.GetSessionAppId(session.handle)
assert app_id == expected_app_id

with pytest.raises(dbus.exceptions.DBusException) as exc_info:
registry_intf.Register(unexpected_app_id, {})
exc_info.match(".*Connection already associated with an application ID.*")
expected_app_id = "org.example.CorrectAppId"
registry_intf.Register(expected_app_id, {})

new_session = self.create_dummy_session(dbus_con)

Expand Down Expand Up @@ -138,4 +135,6 @@ def test_no_reregister(self, portals, dbus_con):

with pytest.raises(dbus.exceptions.DBusException) as exc_info:
registry_intf.Register(expected_app_id, {})
exc_info.match(".*Connection already associated with an application ID.*")
exc_info.match(
".*Connection already associated and registered with an application ID.*"
)

0 comments on commit ea52415

Please sign in to comment.