From 0fb3fdba13b9a34785daa17da32b232032925916 Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Fri, 7 Jun 2024 22:48:54 +0200 Subject: [PATCH] app-info: Make use of the XdpAppInfo subclasses Implement existing functions using the new private fields of XdpAppInfo and the new vfuncs of the class. Also instantiate the subclasses. This allows us to remove the app info kind and the kind specific fields from XdpAppInfo and delegates all of that to the subclasses. --- src/background.c | 4 +- src/location.c | 4 +- src/wallpaper.c | 4 +- src/xdp-app-info.c | 825 ++++----------------------------------------- src/xdp-app-info.h | 4 +- 5 files changed, 72 insertions(+), 769 deletions(-) diff --git a/src/background.c b/src/background.c index c5cdaab9f..671faf666 100644 --- a/src/background.c +++ b/src/background.c @@ -812,9 +812,9 @@ handle_request_background_in_thread_func (GTask *task, guint32 response = 2; g_autoptr(GVariant) results = NULL; g_autoptr(GError) error = NULL; - g_autoptr(GAppInfo) info = NULL; + GAppInfo *info = NULL; - info = xdp_app_info_load_app_info (request->app_info); + info = xdp_app_info_get_gappinfo (request->app_info); app_id = info ? xdp_get_app_id_from_desktop_id (g_app_info_get_id (info)) : g_strdup (id); title = g_strdup_printf (_("Allow %s to run in the background?"), info ? g_app_info_get_display_name (info) : id); diff --git a/src/location.c b/src/location.c index 9a91a7211..fc9620142 100644 --- a/src/location.c +++ b/src/location.c @@ -532,11 +532,9 @@ handle_start_in_thread_func (GTask *task, if (g_strcmp0 (id, "") != 0) { - g_autoptr(GAppInfo) info = NULL; + GAppInfo *info = xdp_app_info_get_gappinfo (request->app_info); const gchar *name = NULL; - info = xdp_app_info_load_app_info (request->app_info); - if (info) { name = g_app_info_get_display_name (G_APP_INFO (info)); diff --git a/src/wallpaper.c b/src/wallpaper.c index c4912a007..255edfa24 100644 --- a/src/wallpaper.c +++ b/src/wallpaper.c @@ -186,11 +186,9 @@ handle_set_wallpaper_in_thread_func (GTask *task, if (g_strcmp0 (id, "") != 0) { - g_autoptr(GAppInfo) info = NULL; + GAppInfo *info = xdp_app_info_get_gappinfo (request->app_info); const gchar *name = NULL; - info = xdp_app_info_load_app_info (request->app_info); - if (info) { name = g_app_info_get_display_name (G_APP_INFO (info)); diff --git a/src/xdp-app-info.c b/src/xdp-app-info.c index cf28402b0..f74a76c0f 100644 --- a/src/xdp-app-info.c +++ b/src/xdp-app-info.c @@ -37,40 +37,22 @@ #include #include "xdp-app-info-private.h" +#include "xdp-app-info-flatpak-private.h" +#include "xdp-app-info-snap-private.h" +#include "xdp-app-info-host-private.h" +#include "xdp-app-info-test-private.h" #define DBUS_NAME_DBUS "org.freedesktop.DBus" #define DBUS_INTERFACE_DBUS DBUS_NAME_DBUS #define DBUS_PATH_DBUS "/org/freedesktop/DBus" -#define FLATPAK_METADATA_GROUP_APPLICATION "Application" -#define FLATPAK_METADATA_KEY_NAME "name" -#define FLATPAK_METADATA_GROUP_INSTANCE "Instance" -#define FLATPAK_METADATA_KEY_APP_PATH "app-path" -#define FLATPAK_METADATA_KEY_ORIGINAL_APP_PATH "original-app-path" -#define FLATPAK_METADATA_KEY_RUNTIME_PATH "runtime-path" -#define FLATPAK_METADATA_KEY_INSTANCE_ID "instance-id" - -#define SNAP_METADATA_GROUP_INFO "Snap Info" -#define SNAP_METADATA_KEY_INSTANCE_NAME "InstanceName" -#define SNAP_METADATA_KEY_DESKTOP_FILE "DesktopFile" -#define SNAP_METADATA_KEY_NETWORK "HasNetworkStatus" - G_LOCK_DEFINE (app_infos); static GHashTable *app_info_by_unique_name; G_DEFINE_QUARK (XdpAppInfo, xdp_app_info_error); -typedef enum -{ - XDP_APP_INFO_KIND_HOST = 0, - XDP_APP_INFO_KIND_FLATPAK = 1, - XDP_APP_INFO_KIND_SNAP = 2, -} XdpAppInfoKind; - typedef struct _XdpAppInfoPrivate { - XdpAppInfoKind kind; - char *engine; char *id; char *instance; @@ -83,21 +65,9 @@ typedef struct _XdpAppInfoPrivate /* pid namespace mapping */ GMutex pidns_lock; ino_t pidns_id; - - union - { - struct - { - GKeyFile *keyfile; - } flatpak; - struct - { - GKeyFile *keyfile; - } snap; - } u; } XdpAppInfoPrivate; -G_DEFINE_TYPE_WITH_PRIVATE (XdpAppInfo, xdp_app_info, G_TYPE_OBJECT) +G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (XdpAppInfo, xdp_app_info, G_TYPE_OBJECT) static void xdp_app_info_dispose (GObject *object) @@ -111,21 +81,6 @@ xdp_app_info_dispose (GObject *object) xdp_close_fd (&priv->pidfd); g_clear_object (&priv->gappinfo); - switch (priv->kind) - { - case XDP_APP_INFO_KIND_FLATPAK: - g_clear_pointer (&priv->u.flatpak.keyfile, g_key_file_free); - break; - - case XDP_APP_INFO_KIND_SNAP: - g_clear_pointer (&priv->u.snap.keyfile, g_key_file_free); - break; - - case XDP_APP_INFO_KIND_HOST: - default: - break; - } - G_OBJECT_CLASS (xdp_app_info_parent_class)->dispose (object); } @@ -168,124 +123,58 @@ xdp_app_info_initialize (XdpAppInfo *app_info, priv->requires_pid_mapping = requires_pid_mapping; } -static XdpAppInfo * -xdp_app_info_new (XdpAppInfoKind kind) -{ - XdpAppInfo *app_info; - XdpAppInfoPrivate *priv; - - app_info = g_object_new (XDP_TYPE_APP_INFO, NULL); - - priv = xdp_app_info_get_instance_private (app_info); - - priv->kind = kind; - priv->pidfd = -1; - - return app_info; -} - gboolean xdp_app_info_is_host (XdpAppInfo *app_info) { - XdpAppInfoPrivate *priv = xdp_app_info_get_instance_private (app_info); - - g_return_val_if_fail (app_info != NULL, FALSE); - - return priv->kind == XDP_APP_INFO_KIND_HOST; + return XDP_IS_APP_INFO_HOST (app_info) || XDP_IS_APP_INFO_TEST (app_info); } const char * xdp_app_info_get_id (XdpAppInfo *app_info) { - XdpAppInfoPrivate *priv = xdp_app_info_get_instance_private (app_info); + XdpAppInfoPrivate *priv; g_return_val_if_fail (app_info != NULL, NULL); + priv = xdp_app_info_get_instance_private (app_info); + return priv->id; } -char * +const char * xdp_app_info_get_instance (XdpAppInfo *app_info) { - XdpAppInfoPrivate *priv = xdp_app_info_get_instance_private (app_info); + XdpAppInfoPrivate *priv; g_return_val_if_fail (app_info != NULL, NULL); - if (priv->kind != XDP_APP_INFO_KIND_FLATPAK) - return NULL; + priv = xdp_app_info_get_instance_private (app_info); - return g_key_file_get_string (priv->u.flatpak.keyfile, - FLATPAK_METADATA_GROUP_INSTANCE, - FLATPAK_METADATA_KEY_INSTANCE_ID, - NULL); + return priv->instance; } GAppInfo * -xdp_app_info_load_app_info (XdpAppInfo *app_info) +xdp_app_info_get_gappinfo (XdpAppInfo *app_info) { - XdpAppInfoPrivate *priv = xdp_app_info_get_instance_private (app_info); - g_autofree char *desktop_id = NULL; + XdpAppInfoPrivate *priv; g_return_val_if_fail (app_info != NULL, NULL); - switch (priv->kind) - { - case XDP_APP_INFO_KIND_FLATPAK: - desktop_id = g_strconcat (priv->id, ".desktop", NULL); - break; - - case XDP_APP_INFO_KIND_SNAP: - desktop_id = g_key_file_get_string (priv->u.snap.keyfile, - SNAP_METADATA_GROUP_INFO, - SNAP_METADATA_KEY_DESKTOP_FILE, - NULL); - break; - - case XDP_APP_INFO_KIND_HOST: - default: - desktop_id = NULL; - break; - } - - if (desktop_id == NULL) - return NULL; + priv = xdp_app_info_get_instance_private (app_info); - return G_APP_INFO (g_desktop_app_info_new (desktop_id)); + return priv->gappinfo; } gboolean xdp_app_info_has_network (XdpAppInfo *app_info) { - XdpAppInfoPrivate *priv = xdp_app_info_get_instance_private (app_info); - gboolean has_network; + XdpAppInfoPrivate *priv; - switch (priv->kind) - { - case XDP_APP_INFO_KIND_FLATPAK: - { - g_auto(GStrv) shared = g_key_file_get_string_list (priv->u.flatpak.keyfile, - "Context", "shared", - NULL, NULL); - if (shared) - has_network = g_strv_contains ((const char * const *)shared, "network"); - else - has_network = FALSE; - } - break; - - case XDP_APP_INFO_KIND_SNAP: - has_network = g_key_file_get_boolean (priv->u.snap.keyfile, - SNAP_METADATA_GROUP_INFO, - SNAP_METADATA_KEY_NETWORK, NULL); - break; - - case XDP_APP_INFO_KIND_HOST: - default: - has_network = TRUE; - break; - } + g_return_val_if_fail (app_info != NULL, TRUE); + + priv = xdp_app_info_get_instance_private (app_info); - return has_network; + return priv->has_network; } gboolean @@ -324,63 +213,11 @@ static char * remap_path (XdpAppInfo *app_info, const char *path) { - XdpAppInfoPrivate *priv = xdp_app_info_get_instance_private (app_info); - - if (priv->kind == XDP_APP_INFO_KIND_FLATPAK) - { - g_autofree char *app_path = g_key_file_get_string (priv->u.flatpak.keyfile, - FLATPAK_METADATA_GROUP_INSTANCE, - FLATPAK_METADATA_KEY_APP_PATH, NULL); - g_autofree char *runtime_path = g_key_file_get_string (priv->u.flatpak.keyfile, - FLATPAK_METADATA_GROUP_INSTANCE, - FLATPAK_METADATA_KEY_RUNTIME_PATH, - NULL); - - /* For apps we translate /app and /usr to the installed locations. - Also, we need to rewrite to drop the /newroot prefix added by - bubblewrap for other files to work. See - https://github.com/projectatomic/bubblewrap/pull/172 - for a bit more information on the /newroot issue. - */ - if (g_str_has_prefix (path, "/newroot/")) - path = path + strlen ("/newroot"); - - if (app_path != NULL && g_str_has_prefix (path, "/app/")) - return g_build_filename (app_path, path + strlen ("/app/"), NULL); - else if (runtime_path != NULL && g_str_has_prefix (path, "/usr/")) - return g_build_filename (runtime_path, path + strlen ("/usr/"), NULL); - else if (g_str_has_prefix (path, "/run/host/usr/")) - return g_build_filename ("/usr", path + strlen ("/run/host/usr/"), NULL); - else if (g_str_has_prefix (path, "/run/host/etc/")) - return g_build_filename ("/etc", path + strlen ("/run/host/etc/"), NULL); - else if (g_str_has_prefix (path, "/run/flatpak/app/")) - return g_build_filename (g_get_user_runtime_dir (), "app", - path + strlen ("/run/flatpak/app/"), NULL); - else if (g_str_has_prefix (path, "/run/flatpak/doc/")) - return g_build_filename (g_get_user_runtime_dir (), "doc", - path + strlen ("/run/flatpak/doc/"), NULL); - else if (g_str_has_prefix (path, "/var/config/")) - return g_build_filename (g_get_home_dir (), ".var", "app", - priv->id, "config", - path + strlen ("/var/config/"), NULL); - else if (g_str_has_prefix (path, "/var/data/")) - return g_build_filename (g_get_home_dir (), ".var", "app", - priv->id, "data", - path + strlen ("/var/data/"), NULL); - } - - return g_strdup (path); -} - -static gboolean -xdp_app_info_supports_opath (XdpAppInfo *app_info) -{ - XdpAppInfoPrivate *priv = xdp_app_info_get_instance_private (app_info); + if (!XDP_APP_INFO_GET_CLASS (app_info)->remap_path) + return g_strdup (path); - return - priv->kind == XDP_APP_INFO_KIND_FLATPAK || - priv->kind == XDP_APP_INFO_KIND_HOST; + return XDP_APP_INFO_GET_CLASS (app_info)->remap_path (app_info, path); } static char * @@ -580,11 +417,11 @@ xdp_app_info_get_path_for_fd (XdpAppInfo *app_info, return NULL; } - if (!xdp_app_info_supports_opath (app_info)) + if (!priv->supports_opath) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - "App \"%s\" of type %d does not support O_PATH fd passing", - priv->id, priv->kind); + "App \"%s\" of type %s does not support O_PATH fd passing", + priv->id, priv->engine); return NULL; } @@ -650,116 +487,6 @@ xdp_app_info_get_path_for_fd (XdpAppInfo *app_info, return g_steal_pointer (&path); } -static char ** -rewrite_commandline (XdpAppInfo *app_info, - const char * const *commandline, - gboolean quote_escape) -{ - XdpAppInfoPrivate *priv = xdp_app_info_get_instance_private (app_info); - - g_autoptr(GPtrArray) args = NULL; - - g_return_val_if_fail (app_info != NULL, NULL); - - if (priv->kind == XDP_APP_INFO_KIND_HOST) - { - int i; - args = g_ptr_array_new_with_free_func (g_free); - for (i = 0; commandline && commandline[i]; i++) - g_ptr_array_add (args, xdp_maybe_quote (commandline[i], quote_escape)); - g_ptr_array_add (args, NULL); - return (char **)g_ptr_array_free (g_steal_pointer (&args), FALSE); - } - else if (priv->kind == XDP_APP_INFO_KIND_FLATPAK) - { - args = g_ptr_array_new_with_free_func (g_free); - - g_ptr_array_add (args, g_strdup ("flatpak")); - g_ptr_array_add (args, g_strdup ("run")); - if (commandline && commandline[0]) - { - int i; - g_autofree char *quoted_command = NULL; - - quoted_command = xdp_maybe_quote (commandline[0], quote_escape); - - g_ptr_array_add (args, g_strdup_printf ("--command=%s", quoted_command)); - - /* Always quote the app ID if quote_escape is enabled to make - * rewriting the file simpler in case the app is renamed. - */ - if (quote_escape) - g_ptr_array_add (args, g_shell_quote (priv->id)); - else - g_ptr_array_add (args, g_strdup (priv->id)); - - for (i = 1; commandline[i]; i++) - g_ptr_array_add (args, xdp_maybe_quote (commandline[i], quote_escape)); - } - else if (quote_escape) - g_ptr_array_add (args, g_shell_quote (priv->id)); - else - g_ptr_array_add (args, g_strdup (priv->id)); - g_ptr_array_add (args, NULL); - - return (char **)g_ptr_array_free (g_steal_pointer (&args), FALSE); - } - else - return NULL; -} - -static char * -get_tryexec_path (XdpAppInfo *app_info) -{ - XdpAppInfoPrivate *priv = xdp_app_info_get_instance_private (app_info); - - g_return_val_if_fail (app_info != NULL, NULL); - - if (priv->kind == XDP_APP_INFO_KIND_FLATPAK) - { - g_autofree char *original_app_path = NULL; - g_autofree char *tryexec_path = NULL; - g_autofree char *app_slash = NULL; - g_autofree char *app_path = NULL; - char *app_slash_pointer; - char *path; - - original_app_path = g_key_file_get_string (priv->u.flatpak.keyfile, - FLATPAK_METADATA_GROUP_INSTANCE, - FLATPAK_METADATA_KEY_ORIGINAL_APP_PATH, NULL); - app_path = g_key_file_get_string (priv->u.flatpak.keyfile, - FLATPAK_METADATA_GROUP_INSTANCE, - FLATPAK_METADATA_KEY_APP_PATH, NULL); - path = original_app_path ? original_app_path : app_path; - - if (path == NULL || *path == '\0') - return NULL; - - app_slash = g_strconcat ("app/", priv->id, NULL); - - app_slash_pointer = strstr (path, app_slash); - if (app_slash_pointer == NULL) - return NULL; - - /* Terminate path after the flatpak installation path such as .local/share/flatpak/ */ - *app_slash_pointer = '\0'; - - /* Find the path to the wrapper script exported by Flatpak, which can be - * used in a desktop file's TryExec= - */ - tryexec_path = g_strconcat (path, "exports/bin/", priv->id, NULL); - if (access (tryexec_path, X_OK) != 0) - { - g_debug ("Wrapper script unexpectedly not executable or nonexistent: %s", tryexec_path); - return NULL; - } - - return g_steal_pointer (&tryexec_path); - } - else - return NULL; -} - gboolean xdp_app_info_validate_autostart (XdpAppInfo *app_info, GKeyFile *keyfile, @@ -769,37 +496,19 @@ xdp_app_info_validate_autostart (XdpAppInfo *app_info, { XdpAppInfoPrivate *priv = xdp_app_info_get_instance_private (app_info); - g_auto(GStrv) cmdv = NULL; - g_autofree char *cmd = NULL; - - g_assert (priv->id); - - cmdv = rewrite_commandline (app_info, - autostart_exec, - FALSE /* don't quote escape */); - if (!cmdv) + if (!priv->id || + !XDP_APP_INFO_GET_CLASS (app_info)->validate_autostart) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Autostart not supported for: %s", priv->id); return FALSE; } - cmd = g_strjoinv (" ", cmdv); - - g_key_file_set_string (keyfile, - G_KEY_FILE_DESKTOP_GROUP, - G_KEY_FILE_DESKTOP_KEY_EXEC, - cmd); - - if (priv->kind == XDP_APP_INFO_KIND_FLATPAK) - { - g_key_file_set_string (keyfile, - G_KEY_FILE_DESKTOP_GROUP, - "X-Flatpak", - priv->id); - } - - return TRUE; + return XDP_APP_INFO_GET_CLASS (app_info)->validate_autostart (app_info, + keyfile, + autostart_exec, + cancellable, + error); } gboolean @@ -808,438 +517,18 @@ xdp_app_info_validate_dynamic_launcher (XdpAppInfo *app_info, GError **error) { XdpAppInfoPrivate *priv = xdp_app_info_get_instance_private (app_info); - g_autofree char *exec = NULL; - g_auto(GStrv) exec_strv = NULL; - g_auto(GStrv) prefixed_exec_strv = NULL; - g_autofree char *prefixed_exec = NULL; - g_autofree char *tryexec_path = NULL; - const char *app_id = xdp_app_info_get_id (app_info); - - exec = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, "Exec", error); - if (exec == NULL) - { - g_set_error (error, - XDG_DESKTOP_PORTAL_ERROR, XDG_DESKTOP_PORTAL_ERROR_INVALID_ARGUMENT, - "Desktop entry given to Install() has no Exec line"); - return FALSE; - } - - if (!g_shell_parse_argv (exec, NULL, &exec_strv, error)) - { - g_set_error (error, - XDG_DESKTOP_PORTAL_ERROR, XDG_DESKTOP_PORTAL_ERROR_INVALID_ARGUMENT, - "Desktop entry given to Install() has invalid Exec line"); - return FALSE; - } - - /* Don't let the app give itself access to host files */ - if (priv->kind == XDP_APP_INFO_KIND_FLATPAK && - g_strv_contains ((const char * const *)exec_strv, "--file-forwarding")) - { - g_set_error (error, - XDG_DESKTOP_PORTAL_ERROR, XDG_DESKTOP_PORTAL_ERROR_INVALID_ARGUMENT, - "Desktop entry given to Install() must not use --file-forwarding"); - return FALSE; - } - - prefixed_exec_strv = rewrite_commandline (app_info, - (const char * const *)exec_strv, - TRUE /* quote escape */); - if (prefixed_exec_strv == NULL) - { - g_set_error (error, - XDG_DESKTOP_PORTAL_ERROR, XDG_DESKTOP_PORTAL_ERROR_FAILED, - "DynamicLauncher install not supported for: %s", app_id); - return FALSE; - } - - prefixed_exec = g_strjoinv (" ", prefixed_exec_strv); - g_key_file_set_value (key_file, G_KEY_FILE_DESKTOP_GROUP, "Exec", prefixed_exec); - - tryexec_path = get_tryexec_path (app_info); - if (tryexec_path != NULL) - g_key_file_set_value (key_file, G_KEY_FILE_DESKTOP_GROUP, "TryExec", tryexec_path); - - if (priv->kind == XDP_APP_INFO_KIND_FLATPAK) - { - /* Flatpak checks for this key */ - g_key_file_set_value (key_file, G_KEY_FILE_DESKTOP_GROUP, "X-Flatpak", app_id); - /* Flatpak removes this one for security */ - g_key_file_remove_key (key_file, G_KEY_FILE_DESKTOP_GROUP, "X-GNOME-Bugzilla-ExtraInfoScript", NULL); - } - - return TRUE; -} - -#ifdef HAVE_LIBSYSTEMD -char * -_xdp_parse_app_id_from_unit_name (const char *unit) -{ - g_autoptr(GRegex) regex1 = NULL; - g_autoptr(GRegex) regex2 = NULL; - g_autoptr(GMatchInfo) match = NULL; - g_autoptr(GError) error = NULL; - g_autofree char *app_id = NULL; - - g_assert (g_str_has_prefix (unit, "app-")); - - /* - * From https://systemd.io/DESKTOP_ENVIRONMENTS/ the format is one of: - * app[-]--.scope - * app[-]--.slice - */ - regex1 = g_regex_new ("^app-(?:[[:alnum:]]+\\-)?(.+?)(?:\\-[[:alnum:]]*)(?:\\.scope|\\.slice)$", 0, 0, &error); - g_assert (error == NULL); - /* - * app[-]--autostart.service -> no longer true since systemd v248 - * app[-]-[@].service - */ - regex2 = g_regex_new ("^app-(?:[[:alnum:]]+\\-)?(.+?)(?:@[[:alnum:]]*|\\-autostart)?\\.service$", 0, 0, &error); - g_assert (error == NULL); - - if (!g_regex_match (regex1, unit, 0, &match)) - g_clear_pointer (&match, g_match_info_unref); - - if (match == NULL && !g_regex_match (regex2, unit, 0, &match)) - g_clear_pointer (&match, g_match_info_unref); - - if (match != NULL) - { - g_autofree char *escaped_app_id = NULL; - /* Unescape the unit name which may have \x hex codes in it, e.g. - * "app-gnome-org.gnome.Evolution\x2dalarm\x2dnotify-2437.scope" - */ - escaped_app_id = g_match_info_fetch (match, 1); - if (cunescape (escaped_app_id, UNESCAPE_RELAX, &app_id) < 0) - app_id = g_strdup (""); - } - else - { - app_id = g_strdup (""); - } - - return g_steal_pointer (&app_id); -} -#endif /* HAVE_LIBSYSTEMD */ - -static void -set_appid_from_pid (XdpAppInfo *app_info, - pid_t pid) -{ - XdpAppInfoPrivate *priv = xdp_app_info_get_instance_private (app_info); - -#ifdef HAVE_LIBSYSTEMD - g_autofree char *unit = NULL; - int res; - - g_return_if_fail (priv->id == NULL); - - res = sd_pid_get_user_unit (pid, &unit); - /* - * The session might not be managed by systemd or there could be an error - * fetching our own systemd units or the unit might not be started by the - * desktop environment (e.g. it's a script run from terminal). - */ - if (res == -ENODATA || res < 0 || !unit || !g_str_has_prefix (unit, "app-")) - { - priv->id = g_strdup (""); - return; - } - - priv->id = _xdp_parse_app_id_from_unit_name (unit); - g_debug ("Assigning app ID \"%s\" to pid %ld which has unit \"%s\"", - priv->id, (long) pid, unit); - -#else - priv->id = g_strdup (""); -#endif /* HAVE_LIBSYSTEMD */ -} - -static gboolean -xdp_app_info_from_flatpak (int pid, - XdpAppInfo **out_app_info, - GError **error) -{ - XdpAppInfoPrivate *priv; - g_autofree char *root_path = NULL; - int root_fd = -1; - int info_fd = -1; - struct stat stat_buf; - g_autoptr(GError) local_error = NULL; - g_autoptr(GMappedFile) mapped = NULL; - g_autoptr(GKeyFile) metadata = NULL; - g_autoptr(XdpAppInfo) app_info = NULL; - const char *group; - g_autofree char *id = NULL; - - root_path = g_strdup_printf ("/proc/%u/root", pid); - root_fd = openat (AT_FDCWD, root_path, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY); - if (root_fd == -1) - { - if (errno == EACCES) - { - struct statfs buf; - - /* Access to the root dir isn't allowed. This can happen if the root is on a fuse - * filesystem, such as in a toolbox container. We will never have a fuse rootfs - * in the flatpak case, so in that case its safe to ignore this and - * continue to detect other types of apps. - */ - if (statfs (root_path, &buf) == 0 && - buf.f_type == 0x65735546) /* FUSE_SUPER_MAGIC */ - { - *out_app_info = NULL; - return TRUE; - } - } - - /* Otherwise, we should be able to open the root dir. Probably the app died and - we're failing due to /proc/$pid not existing. In that case fail instead - of treating this as privileged. */ - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Unable to open %s", root_path); - return FALSE; - } - - metadata = g_key_file_new (); - - info_fd = openat (root_fd, ".flatpak-info", O_RDONLY | O_CLOEXEC | O_NOCTTY); - close (root_fd); - if (info_fd == -1) - { - if (errno == ENOENT) - { - /* No file => on the host, return success */ - *out_app_info = NULL; - return TRUE; - } - - /* Some weird error => failure */ - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Unable to open application info file"); - return FALSE; - } - - if (fstat (info_fd, &stat_buf) != 0 || !S_ISREG (stat_buf.st_mode)) - { - /* Some weird fd => failure */ - close (info_fd); - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Unable to open application info file"); - return FALSE; - } - - mapped = g_mapped_file_new_from_fd (info_fd, FALSE, &local_error); - if (mapped == NULL) - { - close (info_fd); - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Can't map .flatpak-info file: %s", local_error->message); - return FALSE; - } - - if (!g_key_file_load_from_data (metadata, - g_mapped_file_get_contents (mapped), - g_mapped_file_get_length (mapped), - G_KEY_FILE_NONE, &local_error)) - { - close (info_fd); - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Can't load .flatpak-info file: %s", local_error->message); - return FALSE; - } - - group = "Application"; - if (g_key_file_has_group (metadata, "Runtime")) - group = "Runtime"; - - id = g_key_file_get_string (metadata, group, "name", error); - if (id == NULL || !xdp_is_valid_app_id (id)) - { - close (info_fd); - return FALSE; - } - - close (info_fd); - - app_info = xdp_app_info_new (XDP_APP_INFO_KIND_FLATPAK); - - priv = xdp_app_info_get_instance_private (app_info); - - priv->id = g_steal_pointer (&id); - priv->u.flatpak.keyfile = g_steal_pointer (&metadata); - - *out_app_info = g_steal_pointer (&app_info); - return TRUE; -} - -int -_xdp_parse_cgroup_file (FILE *f, - gboolean *is_snap) -{ - ssize_t n; - g_autofree char *id = NULL; - g_autofree char *controller = NULL; - g_autofree char *cgroup = NULL; - size_t id_len = 0, controller_len = 0, cgroup_len = 0; - - g_return_val_if_fail(f != NULL, -1); - g_return_val_if_fail(is_snap != NULL, -1); - - *is_snap = FALSE; - do - { - n = getdelim (&id, &id_len, ':', f); - if (n == -1) break; - n = getdelim (&controller, &controller_len, ':', f); - if (n == -1) break; - n = getdelim (&cgroup, &cgroup_len, '\n', f); - if (n == -1) break; - - /* Only consider the freezer, systemd group or unified cgroup - * hierarchies */ - if ((strcmp (controller, "freezer:") == 0 || - strcmp (controller, "name=systemd:") == 0 || - strcmp (controller, ":") == 0) && - strstr (cgroup, "/snap.") != NULL) - { - *is_snap = TRUE; - break; - } - } - while (n >= 0); - - if (n < 0 && !feof(f)) return -1; - - return 0; -} - -static gboolean -pid_is_snap (pid_t pid, - GError **error) -{ - g_autofree char *cgroup_path = NULL;; - int fd; - FILE *f = NULL; - gboolean is_snap = FALSE; - int err = 0; - - g_return_val_if_fail(pid > 0, FALSE); - - cgroup_path = g_strdup_printf ("/proc/%u/cgroup", (guint) pid); - fd = open (cgroup_path, O_RDONLY | O_CLOEXEC | O_NOCTTY); - if (fd == -1) - { - err = errno; - goto end; - } - - f = fdopen (fd, "r"); - if (f == NULL) - { - err = errno; - goto end; - } - - fd = -1; /* fd is now owned by f */ - - if (_xdp_parse_cgroup_file (f, &is_snap) == -1) - err = errno; - - fclose (f); - -end: - /* Silence ENOENT, treating it as "not a snap" */ - if (err != 0 && err != ENOENT) - { - g_set_error (error, G_IO_ERROR, g_io_error_from_errno (err), - "Could not parse cgroup info for pid %u: %s", (guint) pid, - g_strerror (err)); - } - return is_snap; -} - -static gboolean -xdp_app_info_from_snap (int pid, - int pidfd, - XdpAppInfo **out_app_info, - GError **error) -{ - XdpAppInfoPrivate *priv; - g_autoptr(GError) local_error = NULL; - g_autofree char *pid_str = NULL; - g_autofree char *output = NULL; - g_autoptr(GKeyFile) metadata = NULL; - g_autoptr(XdpAppInfo) app_info = NULL; - g_autofree char *snap_name = NULL; - - /* Check the process's cgroup membership to fail quickly for non-snaps */ - if (!pid_is_snap (pid, error)) - { - *out_app_info = NULL; - return TRUE; - } - pid_str = g_strdup_printf ("%u", (guint) pid); - output = xdp_spawn (error, "snap", "routine", "portal-info", pid_str, NULL); - if (output == NULL) - { - return FALSE; - } - - metadata = g_key_file_new (); - if (!g_key_file_load_from_data (metadata, output, -1, G_KEY_FILE_NONE, &local_error)) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Can't read snap info for pid %u: %s", pid, local_error->message); - return FALSE; - } - - snap_name = g_key_file_get_string (metadata, SNAP_METADATA_GROUP_INFO, - SNAP_METADATA_KEY_INSTANCE_NAME, error); - if (snap_name == NULL) + if (!priv->id || + !XDP_APP_INFO_GET_CLASS (app_info)->validate_dynamic_launcher) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "DynamicLauncher install not supported for: %s", priv->id); return FALSE; } - app_info = xdp_app_info_new (XDP_APP_INFO_KIND_SNAP); - - priv = xdp_app_info_get_instance_private (app_info); - - priv->id = g_strconcat ("snap.", snap_name, NULL); - priv->pidfd = dup (pidfd); - priv->u.snap.keyfile = g_steal_pointer (&metadata); - - *out_app_info = g_steal_pointer (&app_info); - return TRUE; -} - -static XdpAppInfo * -xdp_app_info_from_host (pid_t pid, - int pidfd) -{ - XdpAppInfoPrivate *priv; - XdpAppInfo *app_info; - - app_info = xdp_app_info_new (XDP_APP_INFO_KIND_HOST); - priv = xdp_app_info_get_instance_private (app_info); - - set_appid_from_pid (app_info, pid); - priv->pidfd = dup (pidfd); - return app_info; -} - -static XdpAppInfo * -xdp_app_info_new_test_host (const char *app_id) -{ - XdpAppInfoPrivate *priv; - XdpAppInfo *app_info; - - app_info = xdp_app_info_new (XDP_APP_INFO_KIND_HOST); - priv = xdp_app_info_get_instance_private (app_info); - - priv->id = g_strdup (app_id); - return app_info; + return XDP_APP_INFO_GET_CLASS (app_info)->validate_dynamic_launcher (app_info, + key_file, + error); } static gboolean @@ -1426,6 +715,7 @@ xdp_connection_lookup_app_info_sync (GDBusConnection *connection, g_autoptr(XdpAppInfo) app_info = NULL; xdp_autofd int pidfd = -1; uint32_t pid; + g_autoptr(GError) local_error = NULL; app_info = cache_lookup_app_info_by_sender (sender); if (app_info) @@ -1434,14 +724,31 @@ xdp_connection_lookup_app_info_sync (GDBusConnection *connection, if (!xdp_connection_get_pidfd (connection, sender, cancellable, &pidfd, &pid, error)) return NULL; - if (!xdp_app_info_from_flatpak (pid, &app_info, error)) - return NULL; + app_info = xdp_app_info_flatpak_new (pid, pidfd, &local_error); - if (app_info == NULL && !xdp_app_info_from_snap (pid, pidfd, &app_info, error)) - return NULL; + if (!app_info && !g_error_matches (local_error, XDP_APP_INFO_ERROR, + XDP_APP_INFO_ERROR_WRONG_APP_KIND)) + { + g_propagate_error (error, local_error); + return NULL; + } + g_clear_error (&local_error); if (app_info == NULL) - app_info = xdp_app_info_from_host (pid, pidfd); + app_info = xdp_app_info_snap_new (pid, pidfd, &local_error); + + if (!app_info && !g_error_matches (local_error, XDP_APP_INFO_ERROR, + XDP_APP_INFO_ERROR_WRONG_APP_KIND)) + { + g_propagate_error (error, local_error); + return NULL; + } + g_clear_error (&local_error); + + if (app_info == NULL) + app_info = xdp_app_info_host_new (pid, pidfd); + + g_return_val_if_fail (app_info != NULL, NULL); cache_insert_app_info (sender, app_info); @@ -1461,7 +768,7 @@ xdp_invocation_lookup_app_info_sync (GDBusMethodInvocation *invocation, test_override_app_id = g_getenv ("XDG_DESKTOP_PORTAL_TEST_APP_ID"); if (test_override_app_id) - return xdp_app_info_new_test_host (test_override_app_id); + return xdp_app_info_test_new (test_override_app_id); return xdp_connection_lookup_app_info_sync (connection, sender, cancellable, error); } diff --git a/src/xdp-app-info.h b/src/xdp-app-info.h index 8f80ded19..0c6aea7a4 100644 --- a/src/xdp-app-info.h +++ b/src/xdp-app-info.h @@ -43,9 +43,9 @@ gboolean xdp_app_info_is_host (XdpAppInfo *app_info); const char * xdp_app_info_get_id (XdpAppInfo *app_info); -char * xdp_app_info_get_instance (XdpAppInfo *app_info); +const char * xdp_app_info_get_instance (XdpAppInfo *app_info); -GAppInfo * xdp_app_info_load_app_info (XdpAppInfo *app_info); +GAppInfo * xdp_app_info_get_gappinfo (XdpAppInfo *app_info); gboolean xdp_app_info_has_network (XdpAppInfo *app_info);