mirror of
https://github.com/morgan9e/grd
synced 2026-04-13 16:04:13 +09:00
1422 lines
48 KiB
C
1422 lines
48 KiB
C
/*
|
|
* Copyright (C) 2023 SUSE Software Solutions Germany GmbH
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
* 02111-1307, USA.
|
|
*
|
|
* Written by:
|
|
* Joan Torres <joan.torres@suse.com>
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "grd-daemon-system.h"
|
|
|
|
#include <gio/gunixfdlist.h>
|
|
|
|
#include "grd-context.h"
|
|
#include "grd-daemon.h"
|
|
#include "grd-daemon-utils.h"
|
|
#include "grd-dbus-gdm.h"
|
|
#include "grd-dbus-remote-desktop.h"
|
|
#include "grd-private.h"
|
|
#include "grd-rdp-server.h"
|
|
#include "grd-session-rdp.h"
|
|
#include "grd-settings.h"
|
|
#include "grd-utils.h"
|
|
|
|
#define MAX_HANDOVER_WAIT_TIME_S 30
|
|
|
|
typedef struct
|
|
{
|
|
GrdDBusRemoteDesktopRdpHandover *interface;
|
|
GDBusObjectSkeleton *skeleton;
|
|
char *object_path;
|
|
char *sender_name;
|
|
} HandoverInterface;
|
|
|
|
typedef struct
|
|
{
|
|
GrdDaemonSystem *daemon_system;
|
|
|
|
char *id;
|
|
|
|
GrdSession *session;
|
|
GSocketConnection *socket_connection;
|
|
|
|
HandoverInterface *handover_src;
|
|
HandoverInterface *handover_dst;
|
|
|
|
unsigned int abort_handover_source_id;
|
|
|
|
gboolean is_client_mstsc;
|
|
gboolean use_system_credentials;
|
|
gboolean needs_handover;
|
|
|
|
GrdDBusGdmRemoteDisplay *remote_display;
|
|
} GrdRemoteClient;
|
|
|
|
struct _GrdDaemonSystem
|
|
{
|
|
GrdDaemon parent;
|
|
|
|
GrdDBusGdmRemoteDisplayFactory *remote_display_factory_proxy;
|
|
GDBusObjectManager *display_objects;
|
|
|
|
unsigned int system_grd_name_id;
|
|
GrdDBusRemoteDesktopRdpDispatcher *dispatcher_skeleton;
|
|
GDBusObjectManagerServer *handover_manager_server;
|
|
|
|
GHashTable *remote_clients;
|
|
};
|
|
|
|
G_DEFINE_TYPE (GrdDaemonSystem, grd_daemon_system, GRD_TYPE_DAEMON)
|
|
|
|
typedef void (*ForeachRemoteDisplayCallback) (GrdDaemonSystem *daemon_system,
|
|
GrdDBusGdmRemoteDisplay *remote_display);
|
|
|
|
static void
|
|
grd_remote_client_free (GrdRemoteClient *remote_client);
|
|
|
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GrdRemoteClient, grd_remote_client_free)
|
|
|
|
static void
|
|
on_remote_display_remote_id_changed (GrdDBusGdmRemoteDisplay *remote_display,
|
|
GParamSpec *pspec,
|
|
GrdRemoteClient *remote_client);
|
|
|
|
static void
|
|
on_gdm_remote_display_session_id_changed (GrdDBusGdmRemoteDisplay *remote_display,
|
|
GParamSpec *pspec,
|
|
GrdRemoteClient *remote_client);
|
|
|
|
static void
|
|
disconnect_from_remote_display (GrdRemoteClient *remote_client)
|
|
{
|
|
if (!remote_client->remote_display)
|
|
return;
|
|
|
|
g_signal_handlers_disconnect_by_func (remote_client->remote_display,
|
|
G_CALLBACK (on_remote_display_remote_id_changed),
|
|
remote_client);
|
|
g_signal_handlers_disconnect_by_func (remote_client->remote_display,
|
|
G_CALLBACK (on_gdm_remote_display_session_id_changed),
|
|
remote_client);
|
|
|
|
g_clear_object (&remote_client->remote_display);
|
|
}
|
|
|
|
static gboolean
|
|
grd_daemon_system_is_ready (GrdDaemon *daemon)
|
|
{
|
|
GrdDaemonSystem *daemon_system = GRD_DAEMON_SYSTEM (daemon);
|
|
GDBusProxy *remote_display_factory_proxy =
|
|
G_DBUS_PROXY (daemon_system->remote_display_factory_proxy);
|
|
GDBusObjectManagerClient *display_objects_manager =
|
|
G_DBUS_OBJECT_MANAGER_CLIENT (daemon_system->display_objects);
|
|
GDBusInterfaceSkeleton *dispatcher_skeleton =
|
|
G_DBUS_INTERFACE_SKELETON (daemon_system->dispatcher_skeleton);
|
|
g_autofree const char *gdm_remote_display_factory_name_owner = NULL;
|
|
g_autofree const char *gdm_display_objects_name_owner = NULL;
|
|
g_autoptr (GDBusConnection) manager_connection = NULL;
|
|
GDBusConnection *dispatcher_connection = NULL;
|
|
|
|
if (!daemon_system->remote_display_factory_proxy ||
|
|
!daemon_system->display_objects ||
|
|
!daemon_system->handover_manager_server ||
|
|
!daemon_system->dispatcher_skeleton)
|
|
return FALSE;
|
|
|
|
gdm_remote_display_factory_name_owner = g_dbus_proxy_get_name_owner (
|
|
remote_display_factory_proxy);
|
|
gdm_display_objects_name_owner = g_dbus_object_manager_client_get_name_owner (
|
|
display_objects_manager);
|
|
manager_connection = g_dbus_object_manager_server_get_connection (
|
|
daemon_system->handover_manager_server);
|
|
dispatcher_connection = g_dbus_interface_skeleton_get_connection (
|
|
dispatcher_skeleton);
|
|
|
|
if (!gdm_remote_display_factory_name_owner ||
|
|
!gdm_display_objects_name_owner ||
|
|
!manager_connection ||
|
|
!dispatcher_connection)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
on_handle_take_client (GrdDBusRemoteDesktopRdpHandover *interface,
|
|
GDBusMethodInvocation *invocation,
|
|
GUnixFDList *list,
|
|
GrdRemoteClient *remote_client)
|
|
{
|
|
GSocket *socket = NULL;
|
|
GVariant *fd_variant = NULL;
|
|
g_autoptr (GUnixFDList) fd_list = NULL;
|
|
int fd;
|
|
int fd_idx;
|
|
|
|
g_assert (interface == remote_client->handover_dst->interface);
|
|
|
|
g_debug ("[DaemonSystem] At: %s, received TakeClient call",
|
|
remote_client->handover_dst->object_path);
|
|
|
|
socket = g_socket_connection_get_socket (remote_client->socket_connection);
|
|
fd = g_socket_get_fd (socket);
|
|
|
|
fd_list = g_unix_fd_list_new ();
|
|
fd_idx = g_unix_fd_list_append (fd_list, fd, NULL);
|
|
fd_variant = g_variant_new_handle (fd_idx);
|
|
|
|
grd_dbus_remote_desktop_rdp_handover_complete_take_client (interface,
|
|
invocation,
|
|
fd_list,
|
|
fd_variant);
|
|
|
|
grd_close_connection_and_notify (remote_client->socket_connection);
|
|
g_clear_object (&remote_client->socket_connection);
|
|
g_clear_handle_id (&remote_client->abort_handover_source_id, g_source_remove);
|
|
|
|
grd_dbus_remote_desktop_rdp_handover_set_handover_is_waiting (interface,
|
|
FALSE);
|
|
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
}
|
|
|
|
static void
|
|
abort_handover (gpointer user_data)
|
|
{
|
|
GrdRemoteClient *remote_client = user_data;
|
|
GrdDaemonSystem *daemon_system = remote_client->daemon_system;
|
|
|
|
g_warning ("[DaemonSystem] Aborting handover, removing remote client with "
|
|
"remote id %s", remote_client->id);
|
|
|
|
if (remote_client->session)
|
|
{
|
|
grd_session_rdp_notify_error (GRD_SESSION_RDP (remote_client->session),
|
|
GRD_SESSION_RDP_ERROR_SERVER_REDIRECTION);
|
|
}
|
|
|
|
g_hash_table_remove (daemon_system->remote_clients, remote_client->id);
|
|
}
|
|
|
|
static char *
|
|
get_id_from_routing_token (uint32_t routing_token)
|
|
{
|
|
return g_strdup_printf (REMOTE_DESKTOP_CLIENT_OBJECT_PATH "/%u",
|
|
routing_token);
|
|
}
|
|
|
|
static char *
|
|
get_routing_token_from_id (const char *id)
|
|
{
|
|
g_assert (strlen (id) > strlen (REMOTE_DESKTOP_CLIENT_OBJECT_PATH "/"));
|
|
|
|
return g_strdup (id + strlen (REMOTE_DESKTOP_CLIENT_OBJECT_PATH "/"));
|
|
}
|
|
|
|
static char *
|
|
get_session_id_of_sender (GDBusConnection *connection,
|
|
const char *name,
|
|
GCancellable *cancellable,
|
|
GError **error)
|
|
{
|
|
char *session_id = NULL;
|
|
gboolean success;
|
|
pid_t pid = 0;
|
|
uid_t uid = 0;
|
|
|
|
success = grd_get_pid_of_sender_sync (connection,
|
|
name,
|
|
&pid,
|
|
cancellable,
|
|
error);
|
|
if (!success)
|
|
return NULL;
|
|
|
|
session_id = grd_get_session_id_from_pid (pid);
|
|
if (session_id)
|
|
return session_id;
|
|
|
|
success = grd_get_uid_of_sender_sync (connection,
|
|
name,
|
|
&uid,
|
|
cancellable,
|
|
error);
|
|
if (!success)
|
|
return NULL;
|
|
|
|
session_id = grd_get_session_id_from_uid (uid);
|
|
if (!session_id)
|
|
{
|
|
g_set_error (error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_NOT_FOUND,
|
|
"Could not find a session for user %d",
|
|
(int) uid);
|
|
}
|
|
|
|
return session_id;
|
|
}
|
|
|
|
static char *
|
|
get_handover_object_path_for_call (GrdDaemonSystem *daemon_system,
|
|
GDBusMethodInvocation *invocation,
|
|
GError **error)
|
|
{
|
|
g_autoptr (GDBusObject) object = NULL;
|
|
g_autofree char *object_path = NULL;
|
|
g_autofree char *session_id = NULL;
|
|
GDBusConnection *connection = NULL;
|
|
const char *sender = NULL;
|
|
GCancellable *cancellable;
|
|
|
|
connection = g_dbus_method_invocation_get_connection (invocation);
|
|
sender = g_dbus_method_invocation_get_sender (invocation);
|
|
cancellable = grd_daemon_get_cancellable (GRD_DAEMON (daemon_system));
|
|
|
|
session_id = get_session_id_of_sender (connection,
|
|
sender,
|
|
cancellable,
|
|
error);
|
|
if (!session_id)
|
|
return NULL;
|
|
|
|
object_path = g_strdup_printf ("%s/session%s",
|
|
REMOTE_DESKTOP_HANDOVERS_OBJECT_PATH,
|
|
session_id);
|
|
|
|
object = g_dbus_object_manager_get_object (
|
|
G_DBUS_OBJECT_MANAGER (daemon_system->handover_manager_server),
|
|
object_path);
|
|
if (!object)
|
|
{
|
|
g_set_error (error,
|
|
GRD_DBUS_ERROR,
|
|
GRD_DBUS_ERROR_NO_HANDOVER,
|
|
"No handover interface for session");
|
|
return NULL;
|
|
}
|
|
|
|
return g_steal_pointer (&object_path);
|
|
}
|
|
|
|
static gboolean
|
|
on_authorize_handover_method (GrdDBusRemoteDesktopRdpHandover *interface,
|
|
GDBusMethodInvocation *invocation,
|
|
gpointer user_data)
|
|
{
|
|
GrdRemoteClient *remote_client = user_data;
|
|
g_autofree char *object_path = NULL;
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
g_assert (interface == remote_client->handover_dst->interface);
|
|
g_assert (remote_client->handover_dst->object_path);
|
|
|
|
object_path = get_handover_object_path_for_call (remote_client->daemon_system,
|
|
invocation,
|
|
&error);
|
|
if (!object_path)
|
|
{
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return FALSE;
|
|
}
|
|
|
|
if (g_strcmp0 (object_path, remote_client->handover_dst->object_path) != 0)
|
|
{
|
|
g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR,
|
|
G_DBUS_ERROR_ACCESS_DENIED,
|
|
"Caller using incorrect "
|
|
"handover");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
on_handle_start_handover (GrdDBusRemoteDesktopRdpHandover *interface,
|
|
GDBusMethodInvocation *invocation,
|
|
const char *username,
|
|
const char *password,
|
|
GrdRemoteClient *remote_client)
|
|
{
|
|
GrdDaemon *daemon = GRD_DAEMON (remote_client->daemon_system);
|
|
GrdContext *context = grd_daemon_get_context (daemon);
|
|
GrdSettings *settings = grd_context_get_settings (context);
|
|
g_autofree char *key = NULL;
|
|
g_autofree char *certificate = NULL;
|
|
g_autofree char *routing_token = NULL;
|
|
g_autoptr (GVariant) redirect_variant = NULL;
|
|
GDBusConnection *connection;
|
|
const char *sender;
|
|
|
|
g_assert (interface == remote_client->handover_dst->interface);
|
|
|
|
g_debug ("[DaemonSystem] At: %s, received StartHandover call",
|
|
remote_client->handover_dst->object_path );
|
|
|
|
if (!remote_client->session && !remote_client->handover_src)
|
|
{
|
|
g_warning ("[DaemonSystem] RDP client disconnected during the handover");
|
|
goto err;
|
|
}
|
|
|
|
routing_token = get_routing_token_from_id (remote_client->id);
|
|
|
|
g_object_get (G_OBJECT (settings),
|
|
"rdp-server-cert", &certificate,
|
|
"rdp-server-key", &key,
|
|
NULL);
|
|
|
|
/* The remote client is at daemon-system */
|
|
if (remote_client->session)
|
|
{
|
|
if (!grd_session_rdp_send_server_redirection (
|
|
GRD_SESSION_RDP (remote_client->session),
|
|
routing_token,
|
|
username,
|
|
password,
|
|
certificate))
|
|
goto err;
|
|
}
|
|
else
|
|
{
|
|
connection = g_dbus_interface_skeleton_get_connection (
|
|
G_DBUS_INTERFACE_SKELETON (
|
|
remote_client->handover_src->interface));
|
|
redirect_variant = g_variant_new ("(sss)",
|
|
routing_token,
|
|
username,
|
|
password);
|
|
g_dbus_connection_emit_signal (connection,
|
|
remote_client->handover_src->sender_name,
|
|
remote_client->handover_src->object_path,
|
|
"org.gnome.RemoteDesktop.Rdp.Handover",
|
|
"RedirectClient",
|
|
redirect_variant,
|
|
NULL);
|
|
}
|
|
|
|
sender = g_dbus_method_invocation_get_sender (invocation);
|
|
remote_client->handover_dst->sender_name = g_strdup (sender);
|
|
|
|
grd_dbus_remote_desktop_rdp_handover_complete_start_handover (
|
|
remote_client->handover_dst->interface,
|
|
invocation,
|
|
certificate,
|
|
key);
|
|
|
|
if (remote_client->abort_handover_source_id == 0)
|
|
{
|
|
remote_client->abort_handover_source_id =
|
|
g_timeout_add_seconds_once (MAX_HANDOVER_WAIT_TIME_S,
|
|
abort_handover,
|
|
remote_client);
|
|
}
|
|
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
|
|
err:
|
|
g_dbus_method_invocation_return_error (invocation,
|
|
G_DBUS_ERROR,
|
|
G_DBUS_ERROR_UNKNOWN_OBJECT,
|
|
"Failed to start handover");
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
}
|
|
|
|
static gboolean
|
|
on_handle_get_system_credentials (GrdDBusRemoteDesktopRdpHandover *interface,
|
|
GDBusMethodInvocation *invocation,
|
|
GrdRemoteClient *remote_client)
|
|
{
|
|
GrdDaemon *daemon = GRD_DAEMON (remote_client->daemon_system);
|
|
GrdContext *context = grd_daemon_get_context (daemon);
|
|
GrdSettings *settings = grd_context_get_settings (context);
|
|
g_autofree char *username = NULL;
|
|
g_autofree char *password = NULL;
|
|
|
|
g_assert (interface == remote_client->handover_dst->interface);
|
|
|
|
g_debug ("[DaemonSystem] At: %s, received GetSystemCredentials call",
|
|
remote_client->handover_dst->object_path);
|
|
|
|
if (!remote_client->use_system_credentials)
|
|
{
|
|
g_dbus_method_invocation_return_error (invocation,
|
|
G_DBUS_ERROR,
|
|
G_DBUS_ERROR_IO_ERROR,
|
|
"This handover daemon isn't "
|
|
"allowed to use system credentials");
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
}
|
|
|
|
if (!grd_settings_get_rdp_credentials (settings,
|
|
&username, &password,
|
|
NULL))
|
|
g_assert_not_reached ();
|
|
|
|
grd_dbus_remote_desktop_rdp_handover_complete_get_system_credentials (
|
|
remote_client->handover_dst->interface,
|
|
invocation,
|
|
username,
|
|
password);
|
|
|
|
remote_client->use_system_credentials = FALSE;
|
|
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
}
|
|
|
|
static void
|
|
handover_iface_free (HandoverInterface *handover)
|
|
{
|
|
g_clear_object (&handover->skeleton);
|
|
g_clear_object (&handover->interface);
|
|
g_clear_pointer (&handover->object_path, g_free);
|
|
g_clear_pointer (&handover->sender_name, g_free);
|
|
g_free (handover);
|
|
}
|
|
|
|
static HandoverInterface *
|
|
handover_iface_new (const char *session_id,
|
|
GrdRemoteClient *remote_client)
|
|
{
|
|
HandoverInterface *handover;
|
|
|
|
handover = g_new0 (HandoverInterface, 1);
|
|
|
|
handover->object_path = g_strdup_printf ("%s/session%s",
|
|
REMOTE_DESKTOP_HANDOVERS_OBJECT_PATH,
|
|
session_id);
|
|
|
|
handover->skeleton = g_dbus_object_skeleton_new (handover->object_path);
|
|
|
|
handover->interface = grd_dbus_remote_desktop_rdp_handover_skeleton_new ();
|
|
g_signal_connect (handover->interface, "g-authorize-method",
|
|
G_CALLBACK (on_authorize_handover_method), remote_client);
|
|
g_signal_connect (handover->interface, "handle-start-handover",
|
|
G_CALLBACK (on_handle_start_handover), remote_client);
|
|
g_signal_connect (handover->interface, "handle-take-client",
|
|
G_CALLBACK (on_handle_take_client), remote_client);
|
|
g_signal_connect (handover->interface, "handle-get-system-credentials",
|
|
G_CALLBACK (on_handle_get_system_credentials), remote_client);
|
|
|
|
grd_dbus_remote_desktop_rdp_handover_set_handover_is_waiting (
|
|
handover->interface, remote_client->needs_handover);
|
|
|
|
return handover;
|
|
}
|
|
|
|
static void
|
|
register_handover_iface (GrdRemoteClient *remote_client,
|
|
const char *session_id)
|
|
{
|
|
GrdDaemonSystem *daemon_system = remote_client->daemon_system;
|
|
HandoverInterface *handover;
|
|
|
|
handover = handover_iface_new (session_id, remote_client);
|
|
|
|
g_debug ("[DaemonSystem] Registering handover at: %s",
|
|
handover->object_path);
|
|
|
|
g_dbus_object_manager_server_export (daemon_system->handover_manager_server,
|
|
handover->skeleton);
|
|
|
|
g_dbus_object_skeleton_add_interface (
|
|
handover->skeleton,
|
|
G_DBUS_INTERFACE_SKELETON (handover->interface));
|
|
|
|
g_clear_pointer (&remote_client->handover_src, handover_iface_free);
|
|
remote_client->handover_src = remote_client->handover_dst;
|
|
remote_client->handover_dst = handover;
|
|
}
|
|
|
|
static void
|
|
unregister_handover_iface (GrdRemoteClient *remote_client,
|
|
HandoverInterface *handover)
|
|
{
|
|
GrdDaemonSystem *daemon_system = remote_client->daemon_system;
|
|
|
|
if (!handover)
|
|
return;
|
|
|
|
g_debug ("[DaemonSystem] Unregistering handover at: %s",
|
|
handover->object_path);
|
|
|
|
g_dbus_object_skeleton_remove_interface (
|
|
handover->skeleton,
|
|
G_DBUS_INTERFACE_SKELETON (handover->interface));
|
|
|
|
g_dbus_object_manager_server_unexport (daemon_system->handover_manager_server,
|
|
handover->object_path);
|
|
}
|
|
|
|
static void
|
|
grd_remote_client_free (GrdRemoteClient *remote_client)
|
|
{
|
|
disconnect_from_remote_display (remote_client);
|
|
|
|
g_clear_pointer (&remote_client->id, g_free);
|
|
if (remote_client->socket_connection)
|
|
grd_close_connection_and_notify (remote_client->socket_connection);
|
|
g_clear_object (&remote_client->socket_connection);
|
|
unregister_handover_iface (remote_client, remote_client->handover_src);
|
|
unregister_handover_iface (remote_client, remote_client->handover_dst);
|
|
g_clear_pointer (&remote_client->handover_src, handover_iface_free);
|
|
g_clear_pointer (&remote_client->handover_dst, handover_iface_free);
|
|
g_clear_handle_id (&remote_client->abort_handover_source_id, g_source_remove);
|
|
|
|
g_free (remote_client);
|
|
}
|
|
|
|
static char *
|
|
get_next_available_id (GrdDaemonSystem *daemon_system)
|
|
{
|
|
uint32_t routing_token = 0;
|
|
|
|
while (routing_token == 0)
|
|
{
|
|
g_autofree char *id = NULL;
|
|
|
|
routing_token = g_random_int ();
|
|
id = get_id_from_routing_token (routing_token);
|
|
|
|
if (!g_hash_table_contains (daemon_system->remote_clients, id))
|
|
break;
|
|
|
|
routing_token = 0;
|
|
}
|
|
|
|
return get_id_from_routing_token (routing_token);
|
|
}
|
|
|
|
static void
|
|
session_disposed (GrdRemoteClient *remote_client)
|
|
{
|
|
remote_client->session = NULL;
|
|
}
|
|
|
|
static GrdRemoteClient *
|
|
remote_client_new (GrdDaemonSystem *daemon_system,
|
|
GrdSession *session)
|
|
{
|
|
GrdRemoteClient *remote_client;
|
|
|
|
remote_client = g_new0 (GrdRemoteClient, 1);
|
|
remote_client->id = get_next_available_id (daemon_system);
|
|
remote_client->daemon_system = daemon_system;
|
|
remote_client->needs_handover = session != NULL;
|
|
|
|
if (!session)
|
|
return remote_client;
|
|
|
|
remote_client->is_client_mstsc = grd_session_rdp_is_client_mstsc (GRD_SESSION_RDP (session));
|
|
remote_client->session = session;
|
|
g_object_weak_ref (G_OBJECT (session),
|
|
(GWeakNotify) session_disposed,
|
|
remote_client);
|
|
|
|
remote_client->abort_handover_source_id =
|
|
g_timeout_add_seconds_once (MAX_HANDOVER_WAIT_TIME_S,
|
|
abort_handover,
|
|
remote_client);
|
|
|
|
return remote_client;
|
|
}
|
|
|
|
static void
|
|
on_create_remote_display_finished (GObject *object,
|
|
GAsyncResult *result,
|
|
gpointer user_data)
|
|
{
|
|
GrdDBusGdmRemoteDisplayFactory *proxy =
|
|
GRD_DBUS_GDM_REMOTE_DISPLAY_FACTORY (object);
|
|
GrdRemoteClient *remote_client = user_data;
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
if (!grd_dbus_gdm_remote_display_factory_call_create_remote_display_finish (
|
|
proxy, result, &error))
|
|
{
|
|
g_warning ("[DaemonSystem] Error while calling CreateRemoteDisplay on "
|
|
"DisplayMananger: %s", error->message);
|
|
abort_handover (remote_client);
|
|
}
|
|
}
|
|
|
|
static void
|
|
on_incoming_redirected_connection (GrdRdpServer *rdp_server,
|
|
const char *routing_token_str,
|
|
gboolean requested_rdstls,
|
|
GSocketConnection *connection,
|
|
GrdDaemonSystem *daemon_system)
|
|
{
|
|
g_autofree char *remote_id = NULL;
|
|
GrdRemoteClient *remote_client;
|
|
uint64_t routing_token;
|
|
gboolean success;
|
|
|
|
success = g_ascii_string_to_unsigned (routing_token_str, 10, 0, UINT32_MAX,
|
|
&routing_token, NULL);
|
|
if (!success)
|
|
{
|
|
g_autofree char *encoded_token = NULL;
|
|
|
|
g_warning ("[DaemonSystem] Incoming client connection used "
|
|
"invalid routing token");
|
|
|
|
encoded_token = g_base64_encode ((unsigned char *) routing_token_str,
|
|
strlen (routing_token_str));
|
|
g_debug ("[DaemonSystem] Invalid routing token: %s", encoded_token);
|
|
return;
|
|
}
|
|
|
|
g_debug ("[DaemonSystem] Incoming connection with routing token: %u",
|
|
(uint32_t) routing_token);
|
|
|
|
remote_id = get_id_from_routing_token ((uint32_t) routing_token);
|
|
if (!g_hash_table_lookup_extended (daemon_system->remote_clients,
|
|
remote_id, NULL,
|
|
(gpointer *) &remote_client))
|
|
{
|
|
g_warning ("[DaemonSystem] Could not find routing token on "
|
|
"remote_clients list");
|
|
return;
|
|
}
|
|
|
|
remote_client->socket_connection = g_object_ref (connection);
|
|
remote_client->use_system_credentials = remote_client->is_client_mstsc &&
|
|
!requested_rdstls;
|
|
|
|
g_debug ("[DaemonSystem] At: %s, emitting TakeClientReady signal",
|
|
remote_client->handover_dst->object_path);
|
|
|
|
grd_dbus_remote_desktop_rdp_handover_emit_take_client_ready (
|
|
remote_client->handover_dst->interface,
|
|
remote_client->use_system_credentials);
|
|
}
|
|
|
|
static void
|
|
on_incoming_new_connection (GrdRdpServer *rdp_server,
|
|
GrdSession *session,
|
|
GrdDaemonSystem *daemon_system)
|
|
{
|
|
GCancellable *cancellable =
|
|
grd_daemon_get_cancellable (GRD_DAEMON (daemon_system));
|
|
GrdRemoteClient *remote_client;
|
|
|
|
g_debug ("[DaemonSystem] Incoming connection without routing token");
|
|
|
|
remote_client = remote_client_new (daemon_system, session);
|
|
|
|
g_hash_table_insert (daemon_system->remote_clients,
|
|
remote_client->id,
|
|
remote_client);
|
|
|
|
g_debug ("[DaemonSystem] Creating remote display with remote id: %s",
|
|
remote_client->id);
|
|
|
|
grd_dbus_gdm_remote_display_factory_call_create_remote_display (
|
|
daemon_system->remote_display_factory_proxy,
|
|
remote_client->id,
|
|
cancellable,
|
|
on_create_remote_display_finished,
|
|
remote_client);
|
|
}
|
|
|
|
static void
|
|
inform_configuration_service (void)
|
|
{
|
|
g_autoptr (GrdDBusRemoteDesktopConfigurationRdpServer) configuration = NULL;
|
|
|
|
configuration =
|
|
grd_dbus_remote_desktop_configuration_rdp_server_proxy_new_for_bus_sync (
|
|
G_BUS_TYPE_SYSTEM,
|
|
G_DBUS_PROXY_FLAGS_NONE,
|
|
REMOTE_DESKTOP_CONFIGURATION_BUS_NAME,
|
|
REMOTE_DESKTOP_CONFIGURATION_OBJECT_PATH,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
on_rdp_server_started (GrdDaemonSystem *daemon_system)
|
|
{
|
|
GrdRdpServer *rdp_server =
|
|
grd_daemon_get_rdp_server (GRD_DAEMON (daemon_system));
|
|
|
|
g_signal_connect (rdp_server, "incoming-new-connection",
|
|
G_CALLBACK (on_incoming_new_connection),
|
|
daemon_system);
|
|
g_signal_connect (rdp_server, "incoming-redirected-connection",
|
|
G_CALLBACK (on_incoming_redirected_connection),
|
|
daemon_system);
|
|
|
|
inform_configuration_service ();
|
|
}
|
|
|
|
static void
|
|
on_rdp_server_stopped (GrdDaemonSystem *daemon_system)
|
|
{
|
|
GrdRdpServer *rdp_server =
|
|
grd_daemon_get_rdp_server (GRD_DAEMON (daemon_system));
|
|
|
|
g_signal_handlers_disconnect_by_func (rdp_server,
|
|
G_CALLBACK (on_incoming_new_connection),
|
|
daemon_system);
|
|
g_signal_handlers_disconnect_by_func (rdp_server,
|
|
G_CALLBACK (on_incoming_redirected_connection),
|
|
daemon_system);
|
|
|
|
inform_configuration_service ();
|
|
}
|
|
|
|
static void
|
|
get_handover_object_path_for_call_in_thread (GTask *task,
|
|
gpointer source_object,
|
|
gpointer task_data,
|
|
GCancellable *cancellable)
|
|
{
|
|
GDBusMethodInvocation *invocation = task_data;
|
|
GrdDaemonSystem *daemon_system = source_object;
|
|
GError *error = NULL;
|
|
char *object_path;
|
|
|
|
object_path = get_handover_object_path_for_call (daemon_system,
|
|
invocation,
|
|
&error);
|
|
if (error)
|
|
{
|
|
g_task_return_error (task, error);
|
|
return;
|
|
}
|
|
|
|
g_task_return_pointer (task, object_path, g_free);
|
|
}
|
|
|
|
static void
|
|
get_handover_object_path_for_call_async (gpointer source_object,
|
|
gpointer user_data,
|
|
GAsyncReadyCallback on_finished_callback)
|
|
{
|
|
GrdDaemonSystem *daemon_system = source_object;
|
|
GCancellable *cancellable =
|
|
grd_daemon_get_cancellable (GRD_DAEMON (daemon_system));
|
|
GTask *task;
|
|
|
|
task = g_task_new (daemon_system, cancellable, on_finished_callback, user_data);
|
|
g_task_set_task_data (task, user_data, NULL);
|
|
g_task_run_in_thread (task, get_handover_object_path_for_call_in_thread);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static void
|
|
on_get_handover_object_path_finished (GObject *source_object,
|
|
GAsyncResult *result,
|
|
gpointer user_data)
|
|
{
|
|
GrdDaemonSystem *daemon_system = GRD_DAEMON_SYSTEM (source_object);
|
|
GDBusMethodInvocation *invocation = user_data;
|
|
g_autofree char *object_path = NULL;
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
object_path = g_task_propagate_pointer (G_TASK (result), &error);
|
|
if (error)
|
|
{
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
|
|
grd_dbus_remote_desktop_rdp_dispatcher_complete_request_handover (
|
|
daemon_system->dispatcher_skeleton,
|
|
invocation,
|
|
object_path);
|
|
}
|
|
|
|
static gboolean
|
|
on_handle_request_handover (GrdDBusRemoteDesktopRdpDispatcher *skeleton,
|
|
GDBusMethodInvocation *invocation,
|
|
gpointer user_data)
|
|
{
|
|
GrdDaemonSystem *daemon_system = user_data;
|
|
|
|
get_handover_object_path_for_call_async (daemon_system,
|
|
invocation,
|
|
on_get_handover_object_path_finished);
|
|
|
|
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
|
}
|
|
|
|
static void
|
|
on_system_grd_bus_acquired (GDBusConnection *connection,
|
|
const char *name,
|
|
gpointer user_data)
|
|
{
|
|
GrdDaemonSystem *daemon_system = user_data;
|
|
|
|
g_debug ("[DaemonSystem] Now on system bus");
|
|
|
|
g_dbus_object_manager_server_set_connection (
|
|
daemon_system->handover_manager_server,
|
|
connection);
|
|
|
|
g_dbus_interface_skeleton_export (
|
|
G_DBUS_INTERFACE_SKELETON (daemon_system->dispatcher_skeleton),
|
|
connection,
|
|
REMOTE_DESKTOP_DISPATCHER_OBJECT_PATH,
|
|
NULL);
|
|
|
|
grd_daemon_maybe_enable_services (GRD_DAEMON (daemon_system));
|
|
}
|
|
|
|
static void
|
|
on_system_grd_name_acquired (GDBusConnection *connection,
|
|
const char *name,
|
|
gpointer user_data)
|
|
{
|
|
g_debug ("[DaemonSystem] Owned %s name", name);
|
|
}
|
|
|
|
static void
|
|
on_system_grd_name_lost (GDBusConnection *connection,
|
|
const char *name,
|
|
gpointer user_data)
|
|
{
|
|
g_debug ("[DaemonSystem] Lost owned %s name", name);
|
|
}
|
|
|
|
static void
|
|
on_remote_display_factory_name_owner_changed (GrdDBusGdmRemoteDisplayFactory *remote_display_factory_proxy,
|
|
GParamSpec *pspec,
|
|
GrdDaemonSystem *daemon_system)
|
|
{
|
|
g_autofree const char *name_owner = NULL;
|
|
|
|
g_object_get (G_OBJECT (remote_display_factory_proxy),
|
|
"g-name-owner", &name_owner,
|
|
NULL);
|
|
if (!name_owner)
|
|
{
|
|
grd_daemon_disable_services (GRD_DAEMON (daemon_system));
|
|
return;
|
|
}
|
|
|
|
grd_daemon_maybe_enable_services (GRD_DAEMON (daemon_system));
|
|
}
|
|
|
|
static void
|
|
on_remote_display_factory_proxy_acquired (GObject *object,
|
|
GAsyncResult *result,
|
|
gpointer user_data)
|
|
{
|
|
GrdDaemonSystem *daemon_system = user_data;
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
daemon_system->remote_display_factory_proxy =
|
|
grd_dbus_gdm_remote_display_factory_proxy_new_for_bus_finish (result,
|
|
&error);
|
|
if (!daemon_system->remote_display_factory_proxy)
|
|
{
|
|
g_warning ("[DaemonSystem] Failed to acquire GDM remote display "
|
|
"factory proxy: %s", error->message);
|
|
return;
|
|
}
|
|
|
|
g_signal_connect (G_OBJECT (daemon_system->remote_display_factory_proxy),
|
|
"notify::g-name-owner",
|
|
G_CALLBACK (on_remote_display_factory_name_owner_changed),
|
|
daemon_system);
|
|
|
|
grd_daemon_maybe_enable_services (GRD_DAEMON (daemon_system));
|
|
}
|
|
|
|
static void
|
|
steal_handover_from_client (GrdRemoteClient *new_remote_client,
|
|
GrdRemoteClient *old_remote_client)
|
|
{
|
|
HandoverInterface *handover;
|
|
|
|
handover = g_steal_pointer (&old_remote_client->handover_dst);
|
|
|
|
g_signal_handlers_disconnect_by_func (handover->interface,
|
|
G_CALLBACK (on_authorize_handover_method),
|
|
old_remote_client);
|
|
g_signal_handlers_disconnect_by_func (handover->interface,
|
|
G_CALLBACK (on_handle_start_handover),
|
|
old_remote_client);
|
|
g_signal_handlers_disconnect_by_func (handover->interface,
|
|
G_CALLBACK (on_handle_take_client),
|
|
old_remote_client);
|
|
g_signal_handlers_disconnect_by_func (handover->interface,
|
|
G_CALLBACK (on_handle_get_system_credentials),
|
|
old_remote_client);
|
|
|
|
g_signal_connect (handover->interface, "g-authorize-method",
|
|
G_CALLBACK (on_authorize_handover_method),
|
|
new_remote_client);
|
|
g_signal_connect (handover->interface, "handle-start-handover",
|
|
G_CALLBACK (on_handle_start_handover),
|
|
new_remote_client);
|
|
g_signal_connect (handover->interface, "handle-take-client",
|
|
G_CALLBACK (on_handle_take_client),
|
|
new_remote_client);
|
|
g_signal_connect (handover->interface, "handle-get-system-credentials",
|
|
G_CALLBACK (on_handle_get_system_credentials),
|
|
new_remote_client);
|
|
|
|
g_clear_pointer (&new_remote_client->handover_src, handover_iface_free);
|
|
new_remote_client->handover_src = new_remote_client->handover_dst;
|
|
new_remote_client->handover_dst = handover;
|
|
}
|
|
|
|
static void
|
|
on_remote_display_remote_id_changed (GrdDBusGdmRemoteDisplay *remote_display,
|
|
GParamSpec *pspec,
|
|
GrdRemoteClient *remote_client)
|
|
{
|
|
GrdDaemonSystem *daemon_system = remote_client->daemon_system;
|
|
GrdRemoteClient *new_remote_client;
|
|
const char *remote_id;
|
|
|
|
remote_id = grd_dbus_gdm_remote_display_get_remote_id (remote_display);
|
|
if (!g_hash_table_lookup_extended (daemon_system->remote_clients,
|
|
remote_id, NULL,
|
|
(gpointer *) &new_remote_client))
|
|
{
|
|
g_debug ("[DaemonSystem] GDM set to a remote display a remote "
|
|
"id %s we didn't know about", remote_id);
|
|
return;
|
|
}
|
|
|
|
if (remote_client == new_remote_client)
|
|
return;
|
|
|
|
g_debug ("[DaemonSystem] GDM updated a remote display with a new remote id: "
|
|
"%s", remote_id);
|
|
|
|
disconnect_from_remote_display (new_remote_client);
|
|
new_remote_client->remote_display = g_object_ref (remote_display);
|
|
g_signal_connect (remote_display, "notify::remote-id",
|
|
G_CALLBACK (on_remote_display_remote_id_changed),
|
|
new_remote_client);
|
|
|
|
steal_handover_from_client (new_remote_client, remote_client);
|
|
|
|
grd_dbus_remote_desktop_rdp_handover_set_handover_is_waiting (
|
|
new_remote_client->handover_dst->interface, TRUE);
|
|
|
|
g_hash_table_remove (daemon_system->remote_clients, remote_client->id);
|
|
}
|
|
|
|
static void
|
|
on_gdm_remote_display_session_id_changed (GrdDBusGdmRemoteDisplay *remote_display,
|
|
GParamSpec *pspec,
|
|
GrdRemoteClient *remote_client)
|
|
{
|
|
const char *session_id;
|
|
|
|
session_id = grd_dbus_gdm_remote_display_get_session_id (remote_display);
|
|
|
|
g_signal_handlers_disconnect_by_func (remote_display,
|
|
G_CALLBACK (on_gdm_remote_display_session_id_changed),
|
|
remote_client);
|
|
|
|
if (!session_id || g_str_equal (session_id, ""))
|
|
return;
|
|
|
|
g_debug ("[DaemonSystem] Found a new remote display with remote id: %s "
|
|
"and session: %s",
|
|
remote_client->id,
|
|
session_id);
|
|
|
|
g_signal_connect (remote_display, "notify::remote-id",
|
|
G_CALLBACK (on_remote_display_remote_id_changed),
|
|
remote_client);
|
|
|
|
register_handover_iface (remote_client, session_id);
|
|
}
|
|
|
|
static GrdRemoteClient *
|
|
remote_client_new_from_display (GrdDaemonSystem *daemon_system,
|
|
GrdDBusGdmRemoteDisplay *remote_display)
|
|
{
|
|
g_autoptr (GrdRemoteClient) remote_client = NULL;
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
remote_client = remote_client_new (daemon_system, NULL);
|
|
|
|
if (!grd_dbus_gdm_remote_display_call_set_remote_id_sync (remote_display,
|
|
remote_client->id,
|
|
NULL,
|
|
&error))
|
|
{
|
|
g_warning ("[DaemonSystem] Failed to set remote_id on display: %s",
|
|
error->message);
|
|
return NULL;
|
|
}
|
|
|
|
return g_steal_pointer (&remote_client);
|
|
}
|
|
|
|
static GrdRemoteClient *
|
|
get_remote_client_from_remote_display (GrdDaemonSystem *daemon_system,
|
|
GrdDBusGdmRemoteDisplay *remote_display)
|
|
{
|
|
GrdRemoteClient *remote_client = NULL;
|
|
g_autoptr (GError) error = NULL;
|
|
const char *remote_id;
|
|
|
|
remote_id = grd_dbus_gdm_remote_display_get_remote_id (remote_display);
|
|
if (g_hash_table_lookup_extended (daemon_system->remote_clients,
|
|
remote_id, NULL,
|
|
(gpointer *) &remote_client))
|
|
return remote_client;
|
|
|
|
remote_client = remote_client_new_from_display (daemon_system,
|
|
remote_display);
|
|
if (!remote_client)
|
|
return NULL;
|
|
|
|
g_hash_table_insert (daemon_system->remote_clients,
|
|
remote_client->id,
|
|
remote_client);
|
|
|
|
return remote_client;
|
|
}
|
|
|
|
static void
|
|
register_handover_for_display (GrdDaemonSystem *daemon_system,
|
|
GrdDBusGdmRemoteDisplay *remote_display)
|
|
{
|
|
GrdRemoteClient *remote_client;
|
|
const char *session_id;
|
|
|
|
remote_client = get_remote_client_from_remote_display (daemon_system,
|
|
remote_display);
|
|
if (!remote_client)
|
|
return;
|
|
|
|
disconnect_from_remote_display (remote_client);
|
|
remote_client->remote_display = g_object_ref (remote_display);
|
|
|
|
session_id = grd_dbus_gdm_remote_display_get_session_id (remote_display);
|
|
if (!session_id || strcmp (session_id, "") == 0)
|
|
{
|
|
g_signal_connect (remote_display, "notify::session-id",
|
|
G_CALLBACK (on_gdm_remote_display_session_id_changed),
|
|
remote_client);
|
|
return;
|
|
}
|
|
|
|
g_debug ("[DaemonSystem] Found a new remote display with remote id: %s "
|
|
"and session: %s",
|
|
remote_client->id,
|
|
session_id);
|
|
|
|
g_signal_connect (remote_display, "notify::remote-id",
|
|
G_CALLBACK (on_remote_display_remote_id_changed),
|
|
remote_client);
|
|
|
|
register_handover_iface (remote_client, session_id);
|
|
}
|
|
|
|
static void
|
|
unregister_handover_for_display (GrdDaemonSystem *daemon_system,
|
|
GrdDBusGdmRemoteDisplay *remote_display)
|
|
{
|
|
g_autofree const char *object_path = NULL;
|
|
GrdRemoteClient *remote_client;
|
|
const char *session_id;
|
|
const char *remote_id;
|
|
|
|
remote_id = grd_dbus_gdm_remote_display_get_remote_id (remote_display);
|
|
if (!g_hash_table_lookup_extended (daemon_system->remote_clients,
|
|
remote_id, NULL,
|
|
(gpointer *) &remote_client))
|
|
{
|
|
g_debug ("[DaemonSystem] Tried to unregister handover for a remote "
|
|
"display with remote id %s we didn't know about", remote_id);
|
|
return;
|
|
}
|
|
|
|
session_id = grd_dbus_gdm_remote_display_get_session_id (remote_display);
|
|
if (!session_id || g_str_equal (session_id, ""))
|
|
{
|
|
if (!remote_client->handover_src && !remote_client->handover_dst)
|
|
g_hash_table_remove (daemon_system->remote_clients, remote_id);
|
|
|
|
return;
|
|
}
|
|
|
|
object_path = g_strdup_printf ("%s/session%s",
|
|
REMOTE_DESKTOP_HANDOVERS_OBJECT_PATH,
|
|
session_id);
|
|
|
|
g_debug ("[DaemonSystem] Unregistering handover for remote display with "
|
|
"remote id: %s and session: %s", remote_client->id, session_id);
|
|
|
|
if (remote_client->handover_src)
|
|
{
|
|
if (strcmp (object_path, remote_client->handover_src->object_path) == 0)
|
|
{
|
|
unregister_handover_iface (remote_client, remote_client->handover_src);
|
|
g_clear_pointer (&remote_client->handover_src, handover_iface_free);
|
|
}
|
|
}
|
|
|
|
if (remote_client->handover_dst)
|
|
{
|
|
if (strcmp (object_path, remote_client->handover_dst->object_path) == 0)
|
|
g_hash_table_remove (daemon_system->remote_clients, remote_id);
|
|
}
|
|
}
|
|
|
|
static void
|
|
on_gdm_object_remote_display_interface_changed (GrdDaemonSystem *daemon_system,
|
|
GParamSpec *param_spec,
|
|
GrdDBusGdmObject *object)
|
|
{
|
|
GrdDBusGdmRemoteDisplay *remote_display;
|
|
|
|
remote_display = grd_dbus_gdm_object_peek_remote_display (object);
|
|
if (remote_display)
|
|
register_handover_for_display (daemon_system, remote_display);
|
|
}
|
|
|
|
static void
|
|
foreach_remote_display (GrdDaemonSystem *daemon_system,
|
|
ForeachRemoteDisplayCallback callback)
|
|
{
|
|
GList *objects, *l;
|
|
|
|
objects = g_dbus_object_manager_get_objects (daemon_system->display_objects);
|
|
for (l = objects; l; l = l->next)
|
|
{
|
|
GrdDBusGdmObject *object = l->data;
|
|
GrdDBusGdmRemoteDisplay *remote_display;
|
|
|
|
remote_display = grd_dbus_gdm_object_peek_remote_display (object);
|
|
if (remote_display)
|
|
callback (daemon_system, remote_display);
|
|
}
|
|
|
|
g_list_free_full (objects, g_object_unref);
|
|
}
|
|
|
|
static void
|
|
on_gdm_object_added (GrdDaemonSystem *daemon_system,
|
|
GrdDBusGdmObject *object)
|
|
{
|
|
GrdDBusGdmRemoteDisplay *remote_display;
|
|
|
|
remote_display = grd_dbus_gdm_object_peek_remote_display (object);
|
|
if (remote_display)
|
|
{
|
|
register_handover_for_display (daemon_system, remote_display);
|
|
return;
|
|
}
|
|
|
|
g_signal_connect_object (
|
|
G_OBJECT (object),
|
|
"notify::remote-display",
|
|
G_CALLBACK (on_gdm_object_remote_display_interface_changed),
|
|
daemon_system,
|
|
G_CONNECT_SWAPPED);
|
|
}
|
|
|
|
static void
|
|
on_gdm_object_removed (GrdDaemonSystem *daemon_system,
|
|
GrdDBusGdmObject *object)
|
|
{
|
|
GrdDBusGdmRemoteDisplay *remote_display;
|
|
|
|
remote_display = grd_dbus_gdm_object_peek_remote_display (object);
|
|
if (remote_display)
|
|
unregister_handover_for_display (daemon_system, remote_display);
|
|
|
|
g_signal_handlers_disconnect_by_func (
|
|
G_OBJECT (object),
|
|
G_CALLBACK (on_gdm_object_remote_display_interface_changed),
|
|
daemon_system);
|
|
}
|
|
|
|
static void
|
|
on_gdm_display_objects_name_owner_changed (GDBusObjectManager *display_objects,
|
|
GParamSpec *pspec,
|
|
GrdDaemonSystem *daemon_system)
|
|
{
|
|
g_autofree const char *name_owner = NULL;
|
|
|
|
g_object_get (G_OBJECT (display_objects), "name-owner", &name_owner, NULL);
|
|
if (!name_owner)
|
|
{
|
|
grd_daemon_disable_services (GRD_DAEMON (daemon_system));
|
|
return;
|
|
}
|
|
|
|
grd_daemon_maybe_enable_services (GRD_DAEMON (daemon_system));
|
|
}
|
|
|
|
static void
|
|
on_gdm_object_manager_client_acquired (GObject *source_object,
|
|
GAsyncResult *result,
|
|
gpointer user_data)
|
|
{
|
|
GrdDaemonSystem *daemon_system = user_data;
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
daemon_system->display_objects =
|
|
grd_dbus_gdm_object_manager_client_new_for_bus_finish (result, &error);
|
|
if (!daemon_system->display_objects)
|
|
{
|
|
g_warning ("[DaemonSystem] Error connecting to display manager: %s",
|
|
error->message);
|
|
return;
|
|
}
|
|
|
|
foreach_remote_display (daemon_system, register_handover_for_display);
|
|
|
|
g_signal_connect_object (daemon_system->display_objects,
|
|
"object-added",
|
|
G_CALLBACK (on_gdm_object_added),
|
|
daemon_system,
|
|
G_CONNECT_SWAPPED);
|
|
|
|
g_signal_connect_object (daemon_system->display_objects,
|
|
"object-removed",
|
|
G_CALLBACK (on_gdm_object_removed),
|
|
daemon_system,
|
|
G_CONNECT_SWAPPED);
|
|
|
|
g_signal_connect (G_OBJECT (daemon_system->display_objects),
|
|
"notify::name-owner",
|
|
G_CALLBACK (on_gdm_display_objects_name_owner_changed),
|
|
daemon_system);
|
|
|
|
grd_daemon_maybe_enable_services (GRD_DAEMON (daemon_system));
|
|
}
|
|
|
|
GrdDaemonSystem *
|
|
grd_daemon_system_new (GError **error)
|
|
{
|
|
GrdContext *context;
|
|
|
|
context = grd_context_new (GRD_RUNTIME_MODE_SYSTEM, error);
|
|
if (!context)
|
|
return NULL;
|
|
|
|
return g_object_new (GRD_TYPE_DAEMON_SYSTEM,
|
|
"application-id", GRD_DAEMON_SYSTEM_APPLICATION_ID,
|
|
"flags", G_APPLICATION_IS_SERVICE,
|
|
"context", context,
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
grd_daemon_system_init (GrdDaemonSystem *daemon_system)
|
|
{
|
|
daemon_system->remote_clients =
|
|
g_hash_table_new_full (g_str_hash, g_str_equal,
|
|
NULL, (GDestroyNotify) grd_remote_client_free);
|
|
}
|
|
|
|
static void
|
|
grd_daemon_system_startup (GApplication *app)
|
|
{
|
|
GrdDaemonSystem *daemon_system = GRD_DAEMON_SYSTEM (app);
|
|
GCancellable *cancellable =
|
|
grd_daemon_get_cancellable (GRD_DAEMON (daemon_system));
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
daemon_system->dispatcher_skeleton =
|
|
grd_dbus_remote_desktop_rdp_dispatcher_skeleton_new ();
|
|
g_signal_connect (daemon_system->dispatcher_skeleton,
|
|
"handle-request-handover",
|
|
G_CALLBACK (on_handle_request_handover),
|
|
daemon_system);
|
|
|
|
daemon_system->handover_manager_server =
|
|
g_dbus_object_manager_server_new (REMOTE_DESKTOP_HANDOVERS_OBJECT_PATH);
|
|
|
|
daemon_system->system_grd_name_id =
|
|
g_bus_own_name (G_BUS_TYPE_SYSTEM,
|
|
REMOTE_DESKTOP_BUS_NAME,
|
|
G_BUS_NAME_OWNER_FLAGS_NONE,
|
|
on_system_grd_bus_acquired,
|
|
on_system_grd_name_acquired,
|
|
on_system_grd_name_lost,
|
|
daemon_system, NULL);
|
|
|
|
grd_dbus_gdm_remote_display_factory_proxy_new_for_bus (
|
|
G_BUS_TYPE_SYSTEM,
|
|
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
|
|
GDM_BUS_NAME,
|
|
GDM_REMOTE_DISPLAY_FACTORY_OBJECT_PATH,
|
|
cancellable,
|
|
on_remote_display_factory_proxy_acquired,
|
|
daemon_system);
|
|
|
|
grd_dbus_gdm_object_manager_client_new_for_bus (
|
|
G_BUS_TYPE_SYSTEM,
|
|
G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START,
|
|
GDM_BUS_NAME,
|
|
GDM_OBJECT_MANAGER_OBJECT_PATH,
|
|
cancellable,
|
|
on_gdm_object_manager_client_acquired,
|
|
daemon_system);
|
|
|
|
g_signal_connect (daemon_system, "rdp-server-started",
|
|
G_CALLBACK (on_rdp_server_started), NULL);
|
|
|
|
g_signal_connect (daemon_system, "rdp-server-stopped",
|
|
G_CALLBACK (on_rdp_server_stopped), NULL);
|
|
|
|
G_APPLICATION_CLASS (grd_daemon_system_parent_class)->startup (app);
|
|
}
|
|
|
|
static void
|
|
grd_daemon_system_shutdown (GApplication *app)
|
|
{
|
|
GrdDaemonSystem *daemon_system = GRD_DAEMON_SYSTEM (app);
|
|
|
|
g_clear_pointer (&daemon_system->remote_clients, g_hash_table_unref);
|
|
|
|
g_clear_object (&daemon_system->display_objects);
|
|
g_clear_object (&daemon_system->remote_display_factory_proxy);
|
|
|
|
g_dbus_interface_skeleton_unexport (
|
|
G_DBUS_INTERFACE_SKELETON (daemon_system->dispatcher_skeleton));
|
|
g_clear_object (&daemon_system->dispatcher_skeleton);
|
|
|
|
g_clear_object (&daemon_system->handover_manager_server);
|
|
g_clear_handle_id (&daemon_system->system_grd_name_id,
|
|
g_bus_unown_name);
|
|
|
|
G_APPLICATION_CLASS (grd_daemon_system_parent_class)->shutdown (app);
|
|
}
|
|
|
|
static void
|
|
grd_daemon_system_class_init (GrdDaemonSystemClass *klass)
|
|
{
|
|
GApplicationClass *g_application_class = G_APPLICATION_CLASS (klass);
|
|
GrdDaemonClass *daemon_class = GRD_DAEMON_CLASS (klass);
|
|
|
|
g_application_class->startup = grd_daemon_system_startup;
|
|
g_application_class->shutdown = grd_daemon_system_shutdown;
|
|
|
|
daemon_class->is_daemon_ready = grd_daemon_system_is_ready;
|
|
}
|