flatpak-builder/document-portal/xdp-util.c

205 lines
6.3 KiB
C

#include "config.h"
#include <string.h>
#include <errno.h>
#include <gio/gio.h>
#include "xdp-error.h"
static GHashTable *app_ids;
typedef struct {
char *name;
char *app_id;
gboolean exited;
GList *pending;
} AppIdInfo;
static void
app_id_info_free (AppIdInfo *info)
{
g_free (info->name);
g_free (info->app_id);
g_free (info);
}
static void
ensure_app_ids (void)
{
if (app_ids == NULL)
app_ids = g_hash_table_new_full (g_str_hash, g_str_equal,
NULL, (GDestroyNotify)app_id_info_free);
}
static void
got_credentials_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
AppIdInfo *info = user_data;
g_autoptr (GDBusMessage) reply = NULL;
g_autoptr (GError) error = NULL;
GList *l;
reply = g_dbus_connection_send_message_with_reply_finish (G_DBUS_CONNECTION (source_object),
res, &error);
if (!info->exited && reply != NULL)
{
GVariant *body = g_dbus_message_get_body (reply);
guint32 pid;
g_autofree char *path = NULL;
g_autofree char *content = NULL;
g_variant_get (body, "(u)", &pid);
path = g_strdup_printf ("/proc/%u/cgroup", pid);
if (g_file_get_contents (path, &content, NULL, NULL))
{
gchar **lines = g_strsplit (content, "\n", -1);
int i;
for (i = 0; lines[i] != NULL; i++)
{
if (g_str_has_prefix (lines[i], "1:name=systemd:"))
{
const char *unit = lines[i] + strlen ("1:name=systemd:");
g_autofree char *scope = g_path_get_basename (unit);
if (g_str_has_prefix (scope, "xdg-app-") &&
g_str_has_suffix (scope, ".scope"))
{
const char *name = scope + strlen("xdg-app-");
char *dash = strchr (name, '-');
if (dash != NULL)
{
*dash = 0;
info->app_id = g_strdup (name);
}
}
else
info->app_id = g_strdup ("");
}
}
g_strfreev (lines);
}
}
for (l = info->pending; l != NULL; l = l->next)
{
GTask *task = l->data;
if (info->app_id == NULL)
g_task_return_new_error (task, XDP_ERROR, XDP_ERROR_FAILED,
"Can't find app id");
else
g_task_return_pointer (task, g_strdup (info->app_id), g_free);
}
g_list_free_full (info->pending, g_object_unref);
info->pending = NULL;
if (info->app_id == NULL)
g_hash_table_remove (app_ids, info->name);
}
void
xdp_invocation_lookup_app_id (GDBusMethodInvocation *invocation,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GDBusConnection *connection = g_dbus_method_invocation_get_connection (invocation);
const gchar *sender = g_dbus_method_invocation_get_sender (invocation);
g_autoptr(GTask) task = NULL;
AppIdInfo *info;
task = g_task_new (invocation, cancellable, callback, user_data);
ensure_app_ids ();
info = g_hash_table_lookup (app_ids, sender);
if (info == NULL)
{
info = g_new0 (AppIdInfo, 1);
info->name = g_strdup (sender);
g_hash_table_insert (app_ids, info->name, info);
}
if (info->app_id)
g_task_return_pointer (task, g_strdup (info->app_id), g_free);
else
{
if (info->pending == NULL)
{
g_autoptr (GDBusMessage) msg = g_dbus_message_new_method_call ("org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"GetConnectionUnixProcessID");
g_dbus_message_set_body (msg, g_variant_new ("(s)", sender));
g_dbus_connection_send_message_with_reply (connection, msg,
G_DBUS_SEND_MESSAGE_FLAGS_NONE,
30000,
NULL,
cancellable,
got_credentials_cb,
info);
}
info->pending = g_list_prepend (info->pending, g_object_ref (task));
}
}
char *
xdp_invocation_lookup_app_id_finish (GDBusMethodInvocation *invocation,
GAsyncResult *result,
GError **error)
{
return g_task_propagate_pointer (G_TASK (result), error);
}
static void
name_owner_changed (GDBusConnection *connection,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
const char *name, *from, *to;
g_variant_get (parameters, "(sss)", &name, &from, &to);
ensure_app_ids ();
if (name[0] == ':' &&
strcmp (name, from) == 0 &&
strcmp (to, "") == 0)
{
AppIdInfo *info = g_hash_table_lookup (app_ids, name);
if (info != NULL)
{
info->exited = TRUE;
if (info->pending == NULL)
g_hash_table_remove (app_ids, name);
}
}
}
void
xdp_connection_track_name_owners (GDBusConnection *connection)
{
g_dbus_connection_signal_subscribe (connection,
"org.freedesktop.DBus",
"org.freedesktop.DBus",
"NameOwnerChanged",
"/org/freedesktop/DBus",
NULL,
G_DBUS_SIGNAL_FLAGS_NONE,
name_owner_changed,
NULL, NULL);
}