#include <config.h>
#include <stdlib.h>
#include <glib.h>
#include <gio/gio.h>
#ifdef HAVE_SYSTEMD_SD_LOGIN_H
#include <systemd/sd-login.h>
#endif
#include "pk-dbus.h"
#define PK_DBUS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), PK_TYPE_DBUS, PkDbusPrivate))
struct PkDbusPrivate
{
GDBusConnection *connection;
GDBusProxy *proxy_pid;
GDBusProxy *proxy_uid;
GDBusProxy *proxy_session;
};
static gpointer pk_dbus_object = NULL;
G_DEFINE_TYPE (PkDbus, pk_dbus, G_TYPE_OBJECT)
gboolean
pk_dbus_get_uid_pid (PkDbus *dbus, const gchar *sender, guint32 *uid, guint32 *pid)
{
GVariant *reply_var;
const char *key;
GVariant *value;
gboolean need_pid, need_uid;
g_autoptr(GVariantIter) iter = NULL;
g_autoptr(GError) error = NULL;
g_return_val_if_fail (PK_IS_DBUS (dbus), G_MAXUINT);
g_return_val_if_fail (sender != NULL, G_MAXUINT);
if (g_strcmp0 (sender, ":org.freedesktop.PackageKit") == 0) {
g_debug ("using self-check shortcut");
if (uid != NULL)
*uid = 500;
if (pid != NULL)
*pid = G_MAXUINT32;
return TRUE;
}
if (dbus->priv->proxy_pid == NULL)
return FALSE;
reply_var = g_dbus_proxy_call_sync (dbus->priv->proxy_pid,
"GetConnectionCredentials",
g_variant_new ("(s)",
sender),
G_DBUS_CALL_FLAGS_NONE,
2000,
NULL,
&error);
if (reply_var == NULL) {
g_warning ("Failed to get uid/pid for %s: %s",
sender, error->message);
return FALSE;
}
need_pid = pid != NULL;
need_uid = uid != NULL;
g_variant_get (reply_var, "(a{sv})", &iter);
while (g_variant_iter_loop (iter, "{&sv}", &key, &value)) {
if (need_pid && g_strcmp0 (key, "ProcessID") == 0) {
*pid = g_variant_get_uint32 (value);
need_pid = FALSE;
} else if (need_uid && g_strcmp0 (key, "UnixUserID") == 0) {
*uid = g_variant_get_uint32 (value);
need_uid = FALSE;
}
if (!need_pid && !need_uid) {
g_variant_unref (value);
break;
}
}
return TRUE;
}
guint32
pk_dbus_get_uid (PkDbus *dbus, const gchar *sender)
{
guint uid = G_MAXUINT;
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) value = NULL;
g_return_val_if_fail (PK_IS_DBUS (dbus), G_MAXUINT);
g_return_val_if_fail (sender != NULL, G_MAXUINT);
if (dbus->priv->proxy_uid == NULL)
return G_MAXUINT;
if (g_strcmp0 (sender, ":org.freedesktop.PackageKit") == 0) {
g_debug ("using self-check shortcut");
return 500;
}
value = g_dbus_proxy_call_sync (dbus->priv->proxy_uid,
"GetConnectionUnixUser",
g_variant_new ("(s)",
sender),
G_DBUS_CALL_FLAGS_NONE,
2000,
NULL,
&error);
if (value == NULL) {
g_warning ("Failed to get uid for %s: %s",
sender, error->message);
return G_MAXUINT;
}
g_variant_get (value, "(u)", &uid);
return uid;
}
guint32
pk_dbus_get_pid (PkDbus *dbus, const gchar *sender)
{
guint pid = G_MAXUINT;
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) value = NULL;
g_return_val_if_fail (PK_IS_DBUS (dbus), G_MAXUINT);
g_return_val_if_fail (sender != NULL, G_MAXUINT);
if (g_strcmp0 (sender, ":org.freedesktop.PackageKit") == 0) {
g_debug ("using self-check shortcut");
return G_MAXUINT - 1;
}
if (dbus->priv->proxy_pid == NULL)
return G_MAXUINT;
value = g_dbus_proxy_call_sync (dbus->priv->proxy_pid,
"GetConnectionUnixProcessID",
g_variant_new ("(s)",
sender),
G_DBUS_CALL_FLAGS_NONE,
2000,
NULL,
&error);
if (value == NULL) {
g_warning ("Failed to get pid for %s: %s",
sender, error->message);
return G_MAXUINT;
}
g_variant_get (value, "(u)", &pid);
return pid;
}
#ifdef HAVE_SYSTEMD_SD_LOGIN_H
static gchar *
pk_dbus_make_logind_session_id (const gchar *session)
{
g_assert (session != NULL);
return g_strdup_printf ("/org/freedesktop/logind/session-%s", session);
}
static gchar *
pk_dbus_get_session_systemd (guint pid)
{
g_autofree gchar *session_id = NULL;
uid_t uid;
if (sd_pid_get_session (pid, &session_id) >= 0)
return pk_dbus_make_logind_session_id (session_id);
if (sd_pid_get_owner_uid (pid, &uid) < 0)
return NULL;
if (sd_uid_get_display (uid, &session_id) >= 0)
return pk_dbus_make_logind_session_id (session_id);
return NULL;
}
#endif
gchar *
pk_dbus_get_session (PkDbus *dbus, const gchar *sender)
{
gchar *session = NULL;
#ifndef HAVE_SYSTEMD_SD_LOGIN_H
g_autoptr(GError) error = NULL;
#endif
guint pid;
g_autoptr(GVariant) value = NULL;
g_return_val_if_fail (PK_IS_DBUS (dbus), NULL);
g_return_val_if_fail (sender != NULL, NULL);
if (g_strcmp0 (sender, ":org.freedesktop.PackageKit") == 0) {
g_debug ("using self-check shortcut");
session = g_strdup ("xxx");
goto out;
}
if (dbus->priv->proxy_session == NULL) {
g_warning ("no ConsoleKit, so cannot get session");
goto out;
}
pid = pk_dbus_get_pid (dbus, sender);
if (pid == G_MAXUINT) {
g_warning ("failed to get PID");
goto out;
}
#ifdef HAVE_SYSTEMD_SD_LOGIN_H
session = pk_dbus_get_session_systemd (pid);
if (session == NULL)
g_warning ("failed to get session for pid %u", pid);
#else
value = g_dbus_proxy_call_sync (dbus->priv->proxy_session,
"GetSessionForUnixProcess",
g_variant_new ("(u)",
pid),
G_DBUS_CALL_FLAGS_NONE,
2000,
NULL,
&error);
if (value == NULL) {
g_warning ("Failed to get session for %s: %s",
sender, error->message);
goto out;
}
g_variant_get (value, "(o)", &session);
#endif
out:
return session;
}
static void
pk_dbus_finalize (GObject *object)
{
PkDbus *dbus;
g_return_if_fail (object != NULL);
g_return_if_fail (PK_IS_DBUS (object));
dbus = PK_DBUS (object);
if (dbus->priv->proxy_pid != NULL)
g_object_unref (dbus->priv->proxy_pid);
if (dbus->priv->proxy_uid != NULL)
g_object_unref (dbus->priv->proxy_uid);
if (dbus->priv->proxy_session != NULL)
g_object_unref (dbus->priv->proxy_session);
G_OBJECT_CLASS (pk_dbus_parent_class)->finalize (object);
}
static void
pk_dbus_class_init (PkDbusClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = pk_dbus_finalize;
g_type_class_add_private (klass, sizeof (PkDbusPrivate));
}
gboolean
pk_dbus_connect (PkDbus *dbus, GError **error)
{
if (dbus->priv->connection != NULL)
return TRUE;
dbus->priv->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM,
NULL, error);
if (dbus->priv->connection == NULL) {
g_prefix_error (error, "cannot connect to the system bus: ");
return FALSE;
}
dbus->priv->proxy_pid =
g_dbus_proxy_new_sync (dbus->priv->connection,
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
NULL,
"org.freedesktop.DBus",
"/org/freedesktop/DBus/Bus",
"org.freedesktop.DBus",
NULL,
error);
if (dbus->priv->proxy_pid == NULL) {
g_prefix_error (error, "cannot connect to DBus: ");
return FALSE;
}
dbus->priv->proxy_uid =
g_dbus_proxy_new_sync (dbus->priv->connection,
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
NULL,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
NULL,
error);
if (dbus->priv->proxy_uid == NULL) {
g_prefix_error (error, "cannot connect to DBus: ");
return FALSE;
}
dbus->priv->proxy_session =
g_dbus_proxy_new_sync (dbus->priv->connection,
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
NULL,
"org.freedesktop.ConsoleKit",
"/org/freedesktop/ConsoleKit/Manager",
"org.freedesktop.ConsoleKit.Manager",
NULL,
error);
if (dbus->priv->proxy_session == NULL) {
g_prefix_error (error, "cannot connect to DBus: ");
return FALSE;
}
return TRUE;
}
static void
pk_dbus_init (PkDbus *dbus)
{
dbus->priv = PK_DBUS_GET_PRIVATE (dbus);
}
PkDbus *
pk_dbus_new (void)
{
if (pk_dbus_object != NULL) {
g_object_ref (pk_dbus_object);
} else {
pk_dbus_object = g_object_new (PK_TYPE_DBUS, NULL);
g_object_add_weak_pointer (pk_dbus_object, &pk_dbus_object);
}
return PK_DBUS (pk_dbus_object);
}