Skip to content

Commit

Permalink
app: List apps that use a runtime extension when it's EOL
Browse files Browse the repository at this point in the history
Currently if a runtime extension, e.g.
org.freedesktop.Platform.html5-codecs//18.08 is used by a runtime
org.kde.Platform//5.12 which itself is used by one or more apps, when we
print a message to the user about html5-codecs being EOL, we don't find
any apps using it and don't print any. Fix this by including apps that
indirectly use a runtime extension in the "Applications using this
runtime:" list.

In a later commit we can re-use the helper function added here to add a
confirmation dialog if the user tries to remove a runtime extension
that's being used; currently we just let them remove it.

This is limited to only looking in the current flatpak installation, so
a per-user app using a system-wide runtime extension would not be found.

This is implemented using in-memory caches because otherwise it is
horribly slow; see
#4835 (comment)

Helps: #3531
  • Loading branch information
mwleeds committed Jun 24, 2022
1 parent b243a29 commit da2b4da
Show file tree
Hide file tree
Showing 5 changed files with 258 additions and 17 deletions.
58 changes: 56 additions & 2 deletions app/flatpak-cli-transaction.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ struct _FlatpakCliTransaction
GError *first_operation_error;

GHashTable *eol_actions;
GHashTable *runtime_app_map;
GHashTable *extension_app_map;

int rows;
int cols;
Expand Down Expand Up @@ -727,6 +729,47 @@ print_eol_info_message (FlatpakDir *dir,
}
}

static GPtrArray *
find_reverse_dep_apps (FlatpakTransaction *transaction,
FlatpakDir *dir,
FlatpakDecomposed *ref)
{
FlatpakCliTransaction *self = FLATPAK_CLI_TRANSACTION (transaction);
g_autoptr(GPtrArray) apps = NULL;
g_autoptr(GError) local_error = NULL;

if (flatpak_dir_is_runtime_extension (dir, ref))
{
/* Find apps which are using the ref as an extension directly or as an
* extension of their runtime.
*/
apps = flatpak_dir_list_app_refs_with_runtime_extension (dir,
&self->runtime_app_map,
&self->extension_app_map,
ref, NULL, &local_error);
if (apps == NULL)
{
g_debug ("Unable to list apps using extension %s: %s\n",
flatpak_decomposed_get_ref (ref), local_error->message);
return NULL;
}
}
else
{
/* Find any apps using the runtime directly */
apps = flatpak_dir_list_app_refs_with_runtime (dir, &self->runtime_app_map, ref,
NULL, &local_error);
if (apps == NULL)
{
g_debug ("Unable to find apps using runtime %s: %s\n",
flatpak_decomposed_get_ref (ref), local_error->message);
return NULL;
}
}

return g_steal_pointer (&apps);
}

static gboolean
end_of_lifed_with_rebase (FlatpakTransaction *transaction,
const char *remote,
Expand Down Expand Up @@ -792,12 +835,13 @@ end_of_lifed_with_rebase (FlatpakTransaction *transaction,

if (flatpak_decomposed_is_runtime (ref) && !rebased_to_ref)
{
g_autoptr(GPtrArray) apps = flatpak_dir_list_app_refs_with_runtime (dir, ref, NULL, NULL);
g_autoptr(GPtrArray) apps = find_reverse_dep_apps (transaction, dir, ref);

if (apps && apps->len > 0)
{
g_print (_("Applications using this runtime:\n"));
g_print (" ");
for (int i = 0; i < apps->len; i++)
for (guint i = 0; i < apps->len; i++)
{
FlatpakDecomposed *app_ref = g_ptr_array_index (apps, i);
g_autofree char *id = flatpak_decomposed_dup_id (app_ref);
Expand Down Expand Up @@ -1151,6 +1195,10 @@ transaction_ready_pre_auth (FlatpakTransaction *transaction)
FlatpakTablePrinter *printer;
const char *op_shorthand[] = { "i", "u", "i", "r" };

/* These caches may no longer be valid once the transaction runs */
g_clear_pointer (&self->runtime_app_map, g_hash_table_unref);
g_clear_pointer (&self->extension_app_map, g_hash_table_unref);

if (ops == NULL)
return TRUE;

Expand Down Expand Up @@ -1395,6 +1443,12 @@ flatpak_cli_transaction_finalize (GObject *object)

g_hash_table_unref (self->eol_actions);

if (self->runtime_app_map)
g_hash_table_unref (self->runtime_app_map);

if (self->extension_app_map)
g_hash_table_unref (self->extension_app_map);

if (self->printer)
flatpak_table_printer_free (self->printer);

Expand Down
9 changes: 9 additions & 0 deletions common/flatpak-dir-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -646,10 +646,19 @@ GPtrArray * flatpak_dir_list_refs (Fla
FlatpakKinds kinds,
GCancellable *cancellable,
GError **error);
gboolean flatpak_dir_is_runtime_extension (FlatpakDir *self,
FlatpakDecomposed *ref);
GPtrArray * flatpak_dir_list_app_refs_with_runtime (FlatpakDir *self,
GHashTable **runtime_app_map,
FlatpakDecomposed *runtime_ref,
GCancellable *cancellable,
GError **error);
GPtrArray * flatpak_dir_list_app_refs_with_runtime_extension (FlatpakDir *self,
GHashTable **runtime_app_map,
GHashTable **extension_app_map,
FlatpakDecomposed *runtime_ext_ref,
GCancellable *cancellable,
GError **error);
GVariant * flatpak_dir_read_latest_commit (FlatpakDir *self,
const char *remote,
FlatpakDecomposed *ref,
Expand Down
184 changes: 169 additions & 15 deletions common/flatpak-dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -6686,32 +6686,185 @@ flatpak_dir_list_refs (FlatpakDir *self,
return g_steal_pointer (&refs);
}

GPtrArray *
flatpak_dir_list_app_refs_with_runtime (FlatpakDir *self,
FlatpakDecomposed *runtime_ref,
GCancellable *cancellable,
GError **error)
gboolean
flatpak_dir_is_runtime_extension (FlatpakDir *self,
FlatpakDecomposed *ref)
{
g_autoptr(GBytes) ext_deploy_data = NULL;

if (!flatpak_decomposed_is_runtime (ref))
return FALSE;

/* deploy v4 guarantees extension-of info */
ext_deploy_data = flatpak_dir_get_deploy_data (self, ref, 4, NULL, NULL);
if (ext_deploy_data && flatpak_deploy_data_get_extension_of (ext_deploy_data) != NULL)
return TRUE;

return FALSE;
}

static GHashTable *
flatpak_dir_get_runtime_app_map (FlatpakDir *self,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GHashTable) runtime_app_map = g_hash_table_new_full ((GHashFunc)flatpak_decomposed_hash,
(GEqualFunc)flatpak_decomposed_equal,
(GDestroyNotify)flatpak_decomposed_unref,
(GDestroyNotify)g_ptr_array_unref);
g_autoptr(GPtrArray) app_refs = NULL;
const char *runtime_pref = flatpak_decomposed_get_pref (runtime_ref);
g_autoptr(GPtrArray) apps = g_ptr_array_new_with_free_func ((GDestroyNotify)flatpak_decomposed_unref);

app_refs = flatpak_dir_list_refs (self, FLATPAK_KINDS_APP, NULL, NULL);
for (int i = 0; app_refs != NULL && i < app_refs->len; i++)
app_refs = flatpak_dir_list_refs (self, FLATPAK_KINDS_APP, cancellable, error);
if (app_refs == NULL)
return NULL;

for (guint i = 0; i < app_refs->len; i++)
{
FlatpakDecomposed *app_ref = g_ptr_array_index (app_refs, i);
/* deploy v4 guarantees runtime info */
g_autoptr(GBytes) app_deploy_data = flatpak_dir_get_deploy_data (self, app_ref, 4, NULL, NULL);
g_autoptr(FlatpakDecomposed) runtime_decomposed = NULL;
g_autoptr(GPtrArray) runtime_apps = NULL;
const char *runtime_pref;

if (app_deploy_data == NULL)
continue;

runtime_pref = flatpak_deploy_data_get_runtime (app_deploy_data);
runtime_decomposed = flatpak_decomposed_new_from_pref (FLATPAK_KINDS_RUNTIME, runtime_pref, error);
if (runtime_decomposed == NULL)
return NULL;

runtime_apps = g_hash_table_lookup (runtime_app_map, runtime_decomposed);
if (runtime_apps == NULL)
{
runtime_apps = g_ptr_array_new_with_free_func ((GDestroyNotify)flatpak_decomposed_unref);
g_hash_table_insert (runtime_app_map, flatpak_decomposed_ref (runtime_decomposed), g_ptr_array_ref (runtime_apps));
}
else
g_ptr_array_ref (runtime_apps);

g_ptr_array_add (runtime_apps, flatpak_decomposed_ref (app_ref));
}

return g_steal_pointer (&runtime_app_map);
}

GPtrArray *
flatpak_dir_list_app_refs_with_runtime (FlatpakDir *self,
GHashTable **runtime_app_map,
FlatpakDecomposed *runtime_ref,
GCancellable *cancellable,
GError **error)
{
GPtrArray *apps;

g_assert (runtime_app_map != NULL);

if (*runtime_app_map == NULL)
*runtime_app_map = flatpak_dir_get_runtime_app_map (self, cancellable, error);

if (*runtime_app_map == NULL)
return NULL;

apps = g_hash_table_lookup (*runtime_app_map, runtime_ref);
if (apps == NULL) /* unused runtime */
return g_ptr_array_new_with_free_func ((GDestroyNotify)flatpak_decomposed_unref);

return g_ptr_array_ref (apps);
}

static GHashTable *
flatpak_dir_get_extension_app_map (FlatpakDir *self,
GHashTable *runtime_app_map,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GHashTable) extension_app_map = g_hash_table_new_full ((GHashFunc)flatpak_decomposed_hash,
(GEqualFunc)flatpak_decomposed_equal,
(GDestroyNotify)flatpak_decomposed_unref,
(GDestroyNotify)g_ptr_array_unref);
g_autoptr(GPtrArray) all_refs = NULL;

g_assert (runtime_app_map != NULL);

all_refs = flatpak_dir_list_refs (self, FLATPAK_KINDS_RUNTIME | FLATPAK_KINDS_APP, NULL, NULL);
for (guint i = 0; all_refs != NULL && i < all_refs->len; i++)
{
FlatpakDecomposed *ref = g_ptr_array_index (all_refs, i);
g_autoptr(GPtrArray) related = NULL;
GPtrArray *runtime_apps = NULL;

if (flatpak_decomposed_id_is_subref (ref))
continue;

if (app_deploy_data)
if (flatpak_decomposed_is_runtime (ref))
{
const char *app_runtime = flatpak_deploy_data_get_runtime (app_deploy_data);
if (g_strcmp0 (app_runtime, runtime_pref) == 0)
g_ptr_array_add (apps, flatpak_decomposed_ref (app_ref));
runtime_apps = g_hash_table_lookup (runtime_app_map, ref);
if (runtime_apps == NULL)
continue;
}

related = flatpak_dir_find_local_related (self, ref, NULL, TRUE, cancellable, error);
if (related == NULL)
return NULL;

for (guint j = 0; j < related->len; j++)
{
FlatpakRelated *rel = g_ptr_array_index (related, j);
g_autoptr(GPtrArray) extension_apps = g_hash_table_lookup (extension_app_map, rel->ref);
if (extension_apps == NULL)
{
extension_apps = g_ptr_array_new_with_free_func ((GDestroyNotify)flatpak_decomposed_unref);
g_hash_table_insert (extension_app_map, flatpak_decomposed_ref (rel->ref), g_ptr_array_ref (extension_apps));
}
else
g_ptr_array_ref (extension_apps);

if (flatpak_decomposed_is_runtime (ref))
{
g_assert (runtime_apps);
for (guint k = 0; runtime_apps && k < runtime_apps->len; k++)
g_ptr_array_add (extension_apps, flatpak_decomposed_ref (g_ptr_array_index (runtime_apps, k)));
}
else
g_ptr_array_add (extension_apps, flatpak_decomposed_ref (ref));
}
}

return g_steal_pointer (&apps);
return g_steal_pointer (&extension_app_map);
}

GPtrArray *
flatpak_dir_list_app_refs_with_runtime_extension (FlatpakDir *self,
GHashTable **runtime_app_map,
GHashTable **extension_app_map,
FlatpakDecomposed *runtime_ext_ref,
GCancellable *cancellable,
GError **error)
{
GPtrArray *apps;

g_assert (runtime_app_map != NULL);
g_assert (extension_app_map != NULL);

if (*runtime_app_map == NULL)
*runtime_app_map = flatpak_dir_get_runtime_app_map (self, cancellable, error);

if (*runtime_app_map == NULL)
return NULL;

if (*extension_app_map == NULL)
*extension_app_map = flatpak_dir_get_extension_app_map (self, *runtime_app_map, cancellable, error);

if (*extension_app_map == NULL)
return NULL;

apps = g_hash_table_lookup (*extension_app_map, runtime_ext_ref);
if (apps == NULL) /* unused extension */
return g_ptr_array_new_with_free_func ((GDestroyNotify)flatpak_decomposed_unref);

return g_ptr_array_ref (apps);
}

GVariant *
Expand Down Expand Up @@ -10520,10 +10673,11 @@ flatpak_dir_uninstall (FlatpakDir *self,

if (flatpak_decomposed_is_runtime (ref) && !force_remove)
{
g_autoptr(GHashTable) runtime_app_map = NULL;
g_autoptr(GPtrArray) blocking = NULL;

/* Look for apps that need this runtime */
blocking = flatpak_dir_list_app_refs_with_runtime (self, ref, cancellable, error);
blocking = flatpak_dir_list_app_refs_with_runtime (self, &runtime_app_map, ref, cancellable, error);
if (blocking == NULL)
return FALSE;

Expand Down
7 changes: 7 additions & 0 deletions common/flatpak-utils-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,13 @@ g_hash_table_steal_extended (GHashTable *hash_table,
}
#endif

#if !GLIB_CHECK_VERSION (2, 62, 0)
void g_ptr_array_extend (GPtrArray *array_to_extend,
GPtrArray *array,
GCopyFunc func,
gpointer user_data);
#endif

#if !GLIB_CHECK_VERSION (2, 68, 0)
guint g_string_replace (GString *string,
const gchar *find,
Expand Down
17 changes: 17 additions & 0 deletions common/flatpak-utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -9210,6 +9210,23 @@ running_under_sudo (void)
return FALSE;
}

#if !GLIB_CHECK_VERSION (2, 62, 0)
void
g_ptr_array_extend (GPtrArray *array_to_extend,
GPtrArray *array,
GCopyFunc func,
gpointer user_data)
{
for (gsize i = 0; i < array->len; i++)
{
if (func)
g_ptr_array_add (array_to_extend, func (g_ptr_array_index (array, i), user_data));
else
g_ptr_array_add (array_to_extend, g_ptr_array_index (array, i));
}
}
#endif

#if !GLIB_CHECK_VERSION (2, 68, 0)
/* All this code is backported directly from glib */
guint
Expand Down

0 comments on commit da2b4da

Please sign in to comment.