Files
grd/grd-daemon.c
2026-02-13 13:06:50 +09:00

1249 lines
38 KiB
C

/*
* Copyright (C) 2015 Red Hat Inc.
*
* 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:
* Jonas Ådahl <jadahl@gmail.com>
*/
#include "config.h"
#include "grd-daemon.h"
#include <gio/gio.h>
#include <glib-unix.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include <stdio.h>
#include <stdlib.h>
#include "grd-context.h"
#include "grd-daemon-user.h"
#include "grd-dbus-mutter-remote-desktop.h"
#include "grd-dbus-remote-desktop.h"
#include "grd-private.h"
#include "grd-rdp-server.h"
#include "grd-settings-headless.h"
#include "grd-settings-system.h"
#include "grd-settings-user.h"
#include "grd-types.h"
#include "grd-vnc-server.h"
#ifdef HAVE_LIBSYSTEMD
#include "grd-daemon-handover.h"
#include "grd-daemon-system.h"
#endif /* HAVE_LIBSYSTEMD */
#define RDP_SERVER_RESTART_DELAY_MS 3000
#define DEFAULT_MAX_PARALLEL_CONNECTIONS 10
enum
{
PROP_0,
PROP_CONTEXT,
};
enum
{
MUTTER_PROXY_ACQUIRED,
RDP_SERVER_STARTED,
RDP_SERVER_STOPPED,
N_SIGNALS,
};
static guint signals[N_SIGNALS];
typedef struct _GrdDaemonPrivate
{
GSource *sigint_source;
GSource *sigterm_source;
GCancellable *cancellable;
guint mutter_remote_desktop_watch_name_id;
guint mutter_screen_cast_watch_name_id;
GrdContext *context;
GDBusConnection *connection;
GrdDBusRemoteDesktopOrgGnomeRemoteDesktop *remote_desktop_interface;
#ifdef HAVE_RDP
GrdRdpServer *rdp_server;
unsigned int restart_rdp_server_source_id;
GrdDBusRemoteDesktopRdpServer *other_rdp_server_iface;
unsigned int other_rdp_server_watch_name_id;
#endif
#ifdef HAVE_VNC
GrdVncServer *vnc_server;
#endif
} GrdDaemonPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (GrdDaemon, grd_daemon, G_TYPE_APPLICATION)
#define QUOTE1(a) #a
#define QUOTE(a) QUOTE1(a)
#ifdef HAVE_RDP
static void maybe_start_rdp_server (GrdDaemon *daemon);
#endif
static const GDBusErrorEntry grd_dbus_error_entries[] =
{
{ GRD_DBUS_ERROR_NO_HANDOVER, "org.gnome.RemoteDesktop.Error.NoHandover" },
};
GQuark
grd_dbus_error_quark (void)
{
static gsize quark = 0;
g_dbus_error_register_error_domain ("grd-dbus-error-quark",
&quark,
grd_dbus_error_entries,
G_N_ELEMENTS (grd_dbus_error_entries));
return (GQuark) quark;
}
GCancellable *
grd_daemon_get_cancellable (GrdDaemon *daemon)
{
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
return priv->cancellable;
}
GrdContext *
grd_daemon_get_context (GrdDaemon *daemon)
{
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
return priv->context;
}
#ifdef HAVE_RDP
GrdRdpServer *
grd_daemon_get_rdp_server (GrdDaemon *daemon)
{
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
return priv->rdp_server;
}
#endif /* HAVE_RDP */
static void
export_remote_desktop_interface (GrdDaemon *daemon)
{
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
GrdRuntimeMode runtime_mode = grd_context_get_runtime_mode (priv->context);
priv->remote_desktop_interface =
grd_dbus_remote_desktop_org_gnome_remote_desktop_skeleton_new ();
switch (runtime_mode)
{
case GRD_RUNTIME_MODE_SCREEN_SHARE:
grd_dbus_remote_desktop_org_gnome_remote_desktop_set_runtime_mode (
priv->remote_desktop_interface, "screen-share");
break;
case GRD_RUNTIME_MODE_HEADLESS:
grd_dbus_remote_desktop_org_gnome_remote_desktop_set_runtime_mode (
priv->remote_desktop_interface, "headless");
break;
case GRD_RUNTIME_MODE_SYSTEM:
grd_dbus_remote_desktop_org_gnome_remote_desktop_set_runtime_mode (
priv->remote_desktop_interface, "system");
break;
case GRD_RUNTIME_MODE_HANDOVER:
grd_dbus_remote_desktop_org_gnome_remote_desktop_set_runtime_mode (
priv->remote_desktop_interface, "handover");
break;
}
g_dbus_interface_skeleton_export (
G_DBUS_INTERFACE_SKELETON (priv->remote_desktop_interface),
priv->connection,
REMOTE_DESKTOP_OBJECT_PATH,
NULL);
}
static void
unexport_remote_desktop_interface (GrdDaemon *daemon)
{
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
g_dbus_interface_skeleton_unexport (
G_DBUS_INTERFACE_SKELETON (priv->remote_desktop_interface));
g_clear_object (&priv->remote_desktop_interface);
}
#ifdef HAVE_RDP
static void
export_rdp_server_interface (GrdDaemon *daemon)
{
GrdDBusRemoteDesktopRdpServer *rdp_server_interface;
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
GrdSettings *settings = grd_context_get_settings (priv->context);
rdp_server_interface =
grd_dbus_remote_desktop_rdp_server_skeleton_new ();
grd_dbus_remote_desktop_rdp_server_set_enabled (rdp_server_interface, FALSE);
grd_dbus_remote_desktop_rdp_server_set_port (rdp_server_interface, -1);
g_object_bind_property (settings, "rdp-negotiate-port",
rdp_server_interface, "negotiate-port",
G_BINDING_SYNC_CREATE);
g_object_bind_property (settings, "rdp-server-cert-path",
rdp_server_interface, "tls-cert",
G_BINDING_SYNC_CREATE);
g_object_bind_property (settings, "rdp-server-fingerprint",
rdp_server_interface, "tls-fingerprint",
G_BINDING_SYNC_CREATE);
g_object_bind_property (settings, "rdp-server-key-path",
rdp_server_interface, "tls-key",
G_BINDING_SYNC_CREATE);
g_object_bind_property (settings, "rdp-auth-methods",
rdp_server_interface, "auth-methods",
G_BINDING_SYNC_CREATE);
g_object_bind_property (settings, "rdp-kerberos-keytab",
rdp_server_interface, "kerberos-keytab",
G_BINDING_SYNC_CREATE);
g_object_bind_property (settings, "rdp-view-only",
rdp_server_interface, "view-only",
G_BINDING_SYNC_CREATE);
g_dbus_interface_skeleton_export (
G_DBUS_INTERFACE_SKELETON (rdp_server_interface),
priv->connection,
GRD_RDP_SERVER_OBJECT_PATH,
NULL);
grd_context_set_rdp_server_interface (priv->context, rdp_server_interface);
}
static void
unexport_rdp_server_interface (GrdDaemon *daemon)
{
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
GrdDBusRemoteDesktopRdpServer *rdp_server_interface =
grd_context_get_rdp_server_interface (priv->context);
g_dbus_interface_skeleton_unexport (
G_DBUS_INTERFACE_SKELETON (rdp_server_interface));
grd_context_set_rdp_server_interface (priv->context, NULL);
}
static void
start_rdp_server_when_ready (GrdDaemon *daemon,
gboolean should_start_when_ready)
{
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
GrdSettings *settings = grd_context_get_settings (priv->context);
g_signal_handlers_disconnect_by_func (G_OBJECT (settings),
G_CALLBACK (maybe_start_rdp_server),
daemon);
if (!should_start_when_ready)
return;
g_signal_connect_object (G_OBJECT (settings),
"notify::rdp-server-cert",
G_CALLBACK (maybe_start_rdp_server),
daemon, G_CONNECT_SWAPPED);
g_signal_connect_object (G_OBJECT (settings),
"notify::rdp-server-key",
G_CALLBACK (maybe_start_rdp_server),
daemon, G_CONNECT_SWAPPED);
g_signal_connect_object (G_OBJECT (settings),
"notify::rdp-auth-methods",
G_CALLBACK (maybe_start_rdp_server),
daemon, G_CONNECT_SWAPPED);
g_signal_connect_object (G_OBJECT (settings),
"notify::rdp-kerberos-keytab",
G_CALLBACK (maybe_start_rdp_server),
daemon, G_CONNECT_SWAPPED);
}
static void
stop_rdp_server (GrdDaemon *daemon)
{
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
start_rdp_server_when_ready (daemon, FALSE);
if (!priv->rdp_server)
return;
g_signal_emit (daemon, signals[RDP_SERVER_STOPPED], 0);
grd_rdp_server_stop (priv->rdp_server);
g_clear_object (&priv->other_rdp_server_iface);
g_clear_handle_id (&priv->other_rdp_server_watch_name_id, g_bus_unwatch_name);
g_clear_object (&priv->rdp_server);
g_clear_handle_id (&priv->restart_rdp_server_source_id, g_source_remove);
g_message ("RDP server stopped");
}
static void
on_rdp_server_binding_failed (GrdRdpServer *rdp_server,
GrdDaemon *daemon)
{
stop_rdp_server (daemon);
}
static void
on_other_rdp_server_new_connection (GrdDBusRemoteDesktopRdpServer *interface,
GrdDaemon *daemon)
{
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
g_assert (priv->rdp_server);
grd_rdp_server_stop_sessions (priv->rdp_server);
}
static void
on_remote_desktop_rdp_server_proxy_acquired (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
GrdDaemon *daemon = user_data;
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
g_autoptr (GrdDBusRemoteDesktopRdpServer) proxy = NULL;
g_autoptr (GError) error = NULL;
proxy = grd_dbus_remote_desktop_rdp_server_proxy_new_for_bus_finish (result,
&error);
if (!proxy)
{
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
g_warning ("Failed to create remote desktop rdp server proxy: %s",
error->message);
return;
}
priv->other_rdp_server_iface = g_steal_pointer (&proxy);
g_signal_connect (priv->other_rdp_server_iface, "new-connection",
G_CALLBACK (on_other_rdp_server_new_connection), daemon);
}
static void
on_remote_desktop_rdp_server_name_appeared (GDBusConnection *connection,
const char *name,
const char *name_owner,
gpointer user_data)
{
GrdDaemon *daemon = user_data;
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
grd_dbus_remote_desktop_rdp_server_proxy_new (
connection,
G_DBUS_PROXY_FLAGS_NONE,
name,
GRD_RDP_SERVER_OBJECT_PATH,
priv->cancellable,
on_remote_desktop_rdp_server_proxy_acquired,
daemon);
}
static void
on_remote_desktop_rdp_server_name_vanished (GDBusConnection *connection,
const char *name,
gpointer user_data)
{
GrdDaemon *daemon = user_data;
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
g_clear_object (&priv->other_rdp_server_iface);
}
static void
connect_to_other_rdp_server (GrdDaemon *daemon)
{
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
GrdRuntimeMode runtime_mode = grd_context_get_runtime_mode (priv->context);
const char *bus_name;
if (priv->other_rdp_server_watch_name_id != 0)
return;
if (runtime_mode == GRD_RUNTIME_MODE_HEADLESS)
bus_name = GRD_DAEMON_HANDOVER_APPLICATION_ID;
else if (runtime_mode == GRD_RUNTIME_MODE_HANDOVER)
bus_name = GRD_DAEMON_HEADLESS_APPLICATION_ID;
else
g_assert_not_reached ();
priv->other_rdp_server_watch_name_id =
g_bus_watch_name (G_BUS_TYPE_SESSION,
bus_name,
G_BUS_NAME_WATCHER_FLAGS_NONE,
on_remote_desktop_rdp_server_name_appeared,
on_remote_desktop_rdp_server_name_vanished,
daemon, NULL);
}
static void
start_rdp_server (GrdDaemon *daemon)
{
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
GrdRuntimeMode runtime_mode = grd_context_get_runtime_mode (priv->context);
g_autoptr (GError) error = NULL;
g_assert (!priv->rdp_server);
priv->rdp_server = grd_rdp_server_new (priv->context);
g_signal_connect (priv->rdp_server, "binding-failed",
G_CALLBACK (on_rdp_server_binding_failed), daemon);
if (!grd_rdp_server_start (priv->rdp_server, &error))
{
g_warning ("Failed to start RDP server: %s\n", error->message);
stop_rdp_server (daemon);
}
else
{
g_signal_emit (daemon, signals[RDP_SERVER_STARTED], 0);
g_message ("RDP server started");
if (runtime_mode == GRD_RUNTIME_MODE_HANDOVER ||
runtime_mode == GRD_RUNTIME_MODE_HEADLESS)
connect_to_other_rdp_server (daemon);
}
}
static void
maybe_start_rdp_server (GrdDaemon *daemon)
{
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
GrdSettings *settings = grd_context_get_settings (priv->context);
g_autoptr (GError) error = NULL;
GrdRdpAuthMethods auth_methods = 0;
g_autofree char *kerberos_keytab = NULL;
g_autofree char *certificate = NULL;
g_autofree char *key = NULL;
gboolean rdp_enabled = FALSE;
if (priv->rdp_server)
return;
if (!GRD_DAEMON_GET_CLASS (daemon)->is_daemon_ready (daemon))
{
g_debug ("Daemon not ready, not starting RDP server");
return;
}
g_object_get (G_OBJECT (settings),
"rdp-enabled", &rdp_enabled,
"rdp-auth-methods", &auth_methods,
"rdp-kerberos-keytab", &kerberos_keytab,
"rdp-server-cert", &certificate,
"rdp-server-key", &key,
NULL);
if (!rdp_enabled)
{
g_debug ("RDP not enabled, not starting RDP server");
return;
}
if (!auth_methods)
{
g_warning ("No RDP auth methods configured, not enabling server.");
return;
}
if (auth_methods & GRD_RDP_AUTH_METHOD_KERBEROS &&
!kerberos_keytab)
{
g_debug ("Kerberos keytab not set, not starting RDP server");
return;
}
if ((certificate && key) ||
grd_context_get_runtime_mode (priv->context) == GRD_RUNTIME_MODE_HANDOVER)
{
start_rdp_server (daemon);
}
else
{
g_message ("RDP TLS certificate and key not yet configured properly");
start_rdp_server_when_ready (daemon, TRUE);
}
}
static gboolean
restart_rdp_server (gpointer user_data)
{
GrdDaemon *daemon = user_data;
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
priv->restart_rdp_server_source_id = 0;
maybe_start_rdp_server (daemon);
return G_SOURCE_REMOVE;
}
void
grd_daemon_restart_rdp_server_with_delay (GrdDaemon *daemon)
{
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
if (priv->restart_rdp_server_source_id)
return;
stop_rdp_server (daemon);
priv->restart_rdp_server_source_id =
g_timeout_add (RDP_SERVER_RESTART_DELAY_MS, restart_rdp_server, daemon);
}
#endif /* HAVE_RDP */
#ifdef HAVE_VNC
static gboolean
set_string_from_auth_method_enum (GBinding *binding,
const GValue *source_value,
GValue *target_value,
gpointer user_data)
{
GrdVncAuthMethod src_auth_method = g_value_get_enum (source_value);
switch (src_auth_method)
{
case GRD_VNC_AUTH_METHOD_PROMPT:
g_value_set_string (target_value, "prompt");
break;
case GRD_VNC_AUTH_METHOD_PASSWORD:
g_value_set_string (target_value, "password");
break;
}
return TRUE;
}
static void
export_vnc_server_interface (GrdDaemon *daemon)
{
GrdDBusRemoteDesktopVncServer *vnc_server_interface;
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
GrdSettings *settings = grd_context_get_settings (priv->context);
vnc_server_interface =
grd_dbus_remote_desktop_vnc_server_skeleton_new ();
grd_dbus_remote_desktop_vnc_server_set_enabled (vnc_server_interface, FALSE);
grd_dbus_remote_desktop_vnc_server_set_port (vnc_server_interface, -1);
g_object_bind_property (settings, "vnc-negotiate-port",
vnc_server_interface, "negotiate-port",
G_BINDING_SYNC_CREATE);
g_object_bind_property (settings, "vnc-view-only",
vnc_server_interface, "view-only",
G_BINDING_SYNC_CREATE);
g_object_bind_property_full (settings, "vnc-auth-method",
vnc_server_interface, "auth-method",
G_BINDING_SYNC_CREATE,
set_string_from_auth_method_enum,
NULL, NULL, NULL);
g_dbus_interface_skeleton_export (
G_DBUS_INTERFACE_SKELETON (vnc_server_interface),
priv->connection,
GRD_VNC_SERVER_OBJECT_PATH,
NULL);
grd_context_set_vnc_server_interface (priv->context, vnc_server_interface);
}
static void
unexport_vnc_server_interface (GrdDaemon *daemon)
{
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
GrdDBusRemoteDesktopVncServer *vnc_server_interface =
grd_context_get_vnc_server_interface (priv->context);
g_dbus_interface_skeleton_unexport (
G_DBUS_INTERFACE_SKELETON (vnc_server_interface));
grd_context_set_vnc_server_interface (priv->context, NULL);
}
static void
stop_vnc_server (GrdDaemon *daemon)
{
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
if (!priv->vnc_server)
return;
grd_vnc_server_stop (priv->vnc_server);
g_clear_object (&priv->vnc_server);
g_message ("VNC server stopped");
}
static void
start_vnc_server (GrdDaemon *daemon)
{
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
g_autoptr (GError) error = NULL;
if (priv->vnc_server)
return;
priv->vnc_server = grd_vnc_server_new (priv->context);
if (!grd_vnc_server_start (priv->vnc_server, &error))
{
g_warning ("Failed to initialize VNC server: %s\n", error->message);
stop_vnc_server (daemon);
}
else
{
g_message ("VNC server started");
}
}
#endif /* HAVE_VNC */
static void
export_services_status (GrdDaemon *daemon)
{
export_remote_desktop_interface (daemon);
#ifdef HAVE_RDP
export_rdp_server_interface (daemon);
#endif
#ifdef HAVE_VNC
if (GRD_IS_DAEMON_USER (daemon))
export_vnc_server_interface (daemon);
#endif
}
static void
unexport_services_status (GrdDaemon *daemon)
{
unexport_remote_desktop_interface (daemon);
#ifdef HAVE_RDP
unexport_rdp_server_interface (daemon);
#endif
#ifdef HAVE_VNC
if (GRD_IS_DAEMON_USER (daemon))
unexport_vnc_server_interface (daemon);
#endif
}
void
grd_daemon_maybe_enable_services (GrdDaemon *daemon)
{
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
GrdSettings *settings = grd_context_get_settings (priv->context);
gboolean rdp_enabled;
gboolean vnc_enabled;
if (!GRD_DAEMON_GET_CLASS (daemon)->is_daemon_ready (daemon))
return;
grd_context_notify_daemon_ready (priv->context);
g_object_get (G_OBJECT (settings),
"rdp-enabled", &rdp_enabled,
"vnc-enabled", &vnc_enabled,
NULL);
#ifdef HAVE_RDP
maybe_start_rdp_server (daemon);
#endif
#ifdef HAVE_VNC
if (vnc_enabled)
start_vnc_server (daemon);
#endif
}
void
grd_daemon_disable_services (GrdDaemon *daemon)
{
#ifdef HAVE_RDP
stop_rdp_server (daemon);
#endif
#ifdef HAVE_VNC
stop_vnc_server (daemon);
#endif
}
static void
on_mutter_remote_desktop_proxy_acquired (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
GrdDaemon *daemon = user_data;
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
GrdDBusMutterRemoteDesktop *proxy;
g_autoptr (GError) error = NULL;
proxy = grd_dbus_mutter_remote_desktop_proxy_new_finish (result, &error);
if (!proxy)
{
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
g_warning ("Failed to create remote desktop proxy: %s", error->message);
return;
}
grd_context_set_mutter_remote_desktop_proxy (priv->context, proxy);
g_signal_emit (daemon, signals[MUTTER_PROXY_ACQUIRED], 0);
}
static void
on_mutter_screen_cast_proxy_acquired (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
GrdDaemon *daemon = user_data;
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
GrdDBusMutterScreenCast *proxy;
g_autoptr (GError) error = NULL;
proxy = grd_dbus_mutter_screen_cast_proxy_new_finish (result, &error);
if (!proxy)
{
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
g_warning ("Failed to create screen cast proxy: %s", error->message);
return;
}
grd_context_set_mutter_screen_cast_proxy (priv->context, proxy);
g_signal_emit (daemon, signals[MUTTER_PROXY_ACQUIRED], 0);
}
static void
on_mutter_remote_desktop_name_appeared (GDBusConnection *connection,
const char *name,
const char *name_owner,
gpointer user_data)
{
GrdDaemon *daemon = user_data;
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
grd_dbus_mutter_remote_desktop_proxy_new (connection,
G_DBUS_PROXY_FLAGS_NONE,
MUTTER_REMOTE_DESKTOP_BUS_NAME,
MUTTER_REMOTE_DESKTOP_OBJECT_PATH,
priv->cancellable,
on_mutter_remote_desktop_proxy_acquired,
daemon);
}
static void
on_mutter_remote_desktop_name_vanished (GDBusConnection *connection,
const char *name,
gpointer user_data)
{
GrdDaemon *daemon = user_data;
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
grd_daemon_disable_services (daemon);
grd_context_set_mutter_remote_desktop_proxy (priv->context, NULL);
}
static void
on_mutter_screen_cast_name_appeared (GDBusConnection *connection,
const char *name,
const char *name_owner,
gpointer user_data)
{
GrdDaemon *daemon = user_data;
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
grd_dbus_mutter_screen_cast_proxy_new (connection,
G_DBUS_PROXY_FLAGS_NONE,
MUTTER_SCREEN_CAST_BUS_NAME,
MUTTER_SCREEN_CAST_OBJECT_PATH,
priv->cancellable,
on_mutter_screen_cast_proxy_acquired,
daemon);
}
static void
on_mutter_screen_cast_name_vanished (GDBusConnection *connection,
const char *name,
gpointer user_data)
{
GrdDaemon *daemon = user_data;
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
grd_daemon_disable_services (daemon);
grd_context_set_mutter_screen_cast_proxy (priv->context, NULL);
}
void
grd_daemon_acquire_mutter_dbus_proxies (GrdDaemon *daemon)
{
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
g_clear_handle_id (&priv->mutter_remote_desktop_watch_name_id, g_bus_unwatch_name);
priv->mutter_remote_desktop_watch_name_id =
g_bus_watch_name (G_BUS_TYPE_SESSION,
MUTTER_REMOTE_DESKTOP_BUS_NAME,
G_BUS_NAME_WATCHER_FLAGS_NONE,
on_mutter_remote_desktop_name_appeared,
on_mutter_remote_desktop_name_vanished,
daemon, NULL);
g_clear_handle_id (&priv->mutter_screen_cast_watch_name_id, g_bus_unwatch_name);
priv->mutter_screen_cast_watch_name_id =
g_bus_watch_name (G_BUS_TYPE_SESSION,
MUTTER_SCREEN_CAST_BUS_NAME,
G_BUS_NAME_WATCHER_FLAGS_NONE,
on_mutter_screen_cast_name_appeared,
on_mutter_screen_cast_name_vanished,
daemon, NULL);
}
#ifdef HAVE_RDP
static void
on_rdp_enabled_changed (GrdSettings *settings,
GParamSpec *pspec,
GrdDaemon *daemon)
{
gboolean rdp_enabled;
if (!GRD_DAEMON_GET_CLASS (daemon)->is_daemon_ready (daemon))
return;
g_object_get (G_OBJECT (settings), "rdp-enabled", &rdp_enabled, NULL);
if (rdp_enabled)
maybe_start_rdp_server (daemon);
else
stop_rdp_server (daemon);
}
#endif /* HAVE_RDP */
#ifdef HAVE_VNC
static void
on_vnc_enabled_changed (GrdSettings *settings,
GParamSpec *pspec,
GrdDaemon *daemon)
{
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
gboolean vnc_enabled;
if (!GRD_DAEMON_GET_CLASS (daemon)->is_daemon_ready (daemon))
return;
g_object_get (G_OBJECT (settings), "vnc-enabled", &vnc_enabled, NULL);
if (vnc_enabled)
{
g_return_if_fail (!priv->vnc_server);
start_vnc_server (daemon);
}
else
{
stop_vnc_server (daemon);
}
}
#endif /* HAVE_VNC */
static void
grd_daemon_init (GrdDaemon *daemon)
{
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
priv->cancellable = g_cancellable_new ();
}
static GDBusConnection *
get_daemon_dbus_connection (GrdDaemon *daemon)
{
g_autoptr (GDBusConnection) connection = NULL;
g_autoptr (GError) error = NULL;
#if defined(HAVE_RDP) && defined(HAVE_LIBSYSTEMD)
if (GRD_IS_DAEMON_SYSTEM (daemon))
connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
else
#endif /* HAVE_RDP && HAVE_LIBSYSTEMD */
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
if (!connection)
{
g_warning ("Failing acquiring dbus connection: %s", error->message);
return NULL;
}
return g_steal_pointer (&connection);
}
static void
grd_daemon_startup (GApplication *app)
{
GrdDaemon *daemon = GRD_DAEMON (app);
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
GrdSettings *settings = grd_context_get_settings (priv->context);
priv->connection = get_daemon_dbus_connection (daemon);
export_services_status (daemon);
#ifdef HAVE_RDP
if (GRD_IS_SETTINGS_USER (settings) ||
GRD_IS_SETTINGS_HEADLESS (settings) ||
GRD_IS_SETTINGS_SYSTEM (settings))
{
g_signal_connect (settings, "notify::rdp-enabled",
G_CALLBACK (on_rdp_enabled_changed),
daemon);
}
#endif
#ifdef HAVE_VNC
if (GRD_IS_SETTINGS_USER (settings) ||
GRD_IS_SETTINGS_HEADLESS (settings))
{
g_signal_connect (settings, "notify::vnc-enabled",
G_CALLBACK (on_vnc_enabled_changed),
daemon);
}
#endif
/* Run indefinitely, until told to exit. */
g_application_hold (app);
G_APPLICATION_CLASS (grd_daemon_parent_class)->startup (app);
}
static void
grd_daemon_shutdown (GApplication *app)
{
GrdDaemon *daemon = GRD_DAEMON (app);
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
g_cancellable_cancel (priv->cancellable);
g_clear_object (&priv->cancellable);
grd_daemon_disable_services (daemon);
unexport_services_status (daemon);
g_clear_object (&priv->connection);
grd_context_set_mutter_remote_desktop_proxy (priv->context, NULL);
g_clear_handle_id (&priv->mutter_remote_desktop_watch_name_id, g_bus_unwatch_name);
grd_context_set_mutter_screen_cast_proxy (priv->context, NULL);
g_clear_handle_id (&priv->mutter_screen_cast_watch_name_id, g_bus_unwatch_name);
g_clear_object (&priv->context);
if (priv->sigterm_source)
{
g_source_destroy (priv->sigterm_source);
g_clear_pointer (&priv->sigterm_source, g_source_unref);
}
if (priv->sigint_source)
{
g_source_destroy (priv->sigint_source);
g_clear_pointer (&priv->sigint_source, g_source_unref);
}
G_APPLICATION_CLASS (grd_daemon_parent_class)->shutdown (app);
}
static void
grd_daemon_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GrdDaemon *daemon = GRD_DAEMON (object);
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
switch (prop_id)
{
case PROP_CONTEXT:
g_value_set_object (value, priv->context);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
grd_daemon_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GrdDaemon *daemon = GRD_DAEMON (object);
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
switch (prop_id)
{
case PROP_CONTEXT:
priv->context = g_value_get_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
grd_daemon_class_init (GrdDaemonClass *klass)
{
GApplicationClass *g_application_class = G_APPLICATION_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
g_application_class->startup = grd_daemon_startup;
g_application_class->shutdown = grd_daemon_shutdown;
object_class->get_property = grd_daemon_get_property;
object_class->set_property = grd_daemon_set_property;
g_object_class_install_property (object_class,
PROP_CONTEXT,
g_param_spec_object ("context",
"GrdContext",
"The GrdContext instance",
GRD_TYPE_CONTEXT,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
signals[MUTTER_PROXY_ACQUIRED] = g_signal_new ("mutter-proxy-acquired",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
signals[RDP_SERVER_STARTED] = g_signal_new ("rdp-server-started",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
signals[RDP_SERVER_STOPPED] = g_signal_new ("rdp-server-stopped",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
grd_dbus_error_quark ();
}
static void
activate_terminate (GAction *action,
GVariant *parameter,
GrdDaemon *daemon)
{
g_application_release (G_APPLICATION (daemon));
}
static void
add_actions (GApplication *app)
{
g_autoptr(GSimpleAction) action = NULL;
action = g_simple_action_new ("terminate", NULL);
g_signal_connect (action, "activate", G_CALLBACK (activate_terminate), app);
g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (action));
}
static gboolean
sigint_terminate_daemon (gpointer user_data)
{
GrdDaemon *daemon = user_data;
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
g_debug ("Received SIGINT signal. Exiting...");
g_clear_pointer (&priv->sigint_source, g_source_unref);
g_application_release (G_APPLICATION (daemon));
return G_SOURCE_REMOVE;
}
static gboolean
sigterm_terminate_daemon (gpointer user_data)
{
GrdDaemon *daemon = user_data;
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
g_debug ("Received SIGTERM signal. Exiting...");
g_clear_pointer (&priv->sigterm_source, g_source_unref);
g_application_release (G_APPLICATION (daemon));
return G_SOURCE_REMOVE;
}
static void
register_signals (GrdDaemon *daemon)
{
GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon);
priv->sigint_source = g_unix_signal_source_new (SIGINT);
g_source_set_callback (priv->sigint_source, sigint_terminate_daemon,
daemon, NULL);
g_source_attach (priv->sigint_source, NULL);
priv->sigterm_source = g_unix_signal_source_new (SIGTERM);
g_source_set_callback (priv->sigterm_source, sigterm_terminate_daemon,
daemon, NULL);
g_source_attach (priv->sigterm_source, NULL);
}
static int
count_trues (int n_args, ...)
{
va_list booleans;
int booleans_count = 0;
va_start (booleans, n_args);
for (int i = 0; i < n_args; ++i)
booleans_count += va_arg (booleans, gboolean) ? 1 : 0;
return booleans_count;
}
int
main (int argc, char **argv)
{
GrdContext *context;
GrdSettings *settings;
gboolean print_version = FALSE;
gboolean headless = FALSE;
gboolean system = FALSE;
gboolean handover = FALSE;
int rdp_port = -1;
int vnc_port = -1;
int max_parallel_connections = DEFAULT_MAX_PARALLEL_CONNECTIONS;
GOptionEntry entries[] = {
{ "version", 0, 0, G_OPTION_ARG_NONE, &print_version,
"Print version", NULL },
{ "headless", 0, 0, G_OPTION_ARG_NONE, &headless,
"Run in headless mode", NULL },
#if defined(HAVE_RDP) && defined(HAVE_LIBSYSTEMD)
{ "system", 0, 0, G_OPTION_ARG_NONE, &system,
"Run in headless mode as a system g-r-d service", NULL },
{ "handover", 0, 0, G_OPTION_ARG_NONE, &handover,
"Run in headless mode taking a connection from system g-r-d service", NULL },
#endif /* HAVE_RDP && HAVE_LIBSYSTEMD */
{ "rdp-port", 0, 0, G_OPTION_ARG_INT, &rdp_port,
"RDP port", NULL },
{ "vnc-port", 0, 0, G_OPTION_ARG_INT, &vnc_port,
"VNC port", NULL },
{ "max-parallel-connections", 0, 0,
G_OPTION_ARG_INT, &max_parallel_connections,
"Max number of parallel connections (0 for unlimited, "
"default: " QUOTE(DEFAULT_MAX_PARALLEL_CONNECTIONS) ")", NULL },
{ NULL }
};
g_autoptr (GOptionContext) option_context = NULL;
g_autoptr (GrdDaemon) daemon = NULL;
g_autoptr (GError) error = NULL;
GrdRuntimeMode runtime_mode;
g_set_application_name (_("GNOME Remote Desktop"));
option_context = g_option_context_new (NULL);
g_option_context_add_main_entries (option_context, entries, GETTEXT_PACKAGE);
if (!g_option_context_parse (option_context, &argc, &argv, &error))
{
g_printerr ("Invalid option: %s\n", error->message);
return EXIT_FAILURE;
}
if (print_version)
{
g_print ("GNOME Remote Desktop %s\n", VERSION);
return EXIT_SUCCESS;
}
if (count_trues (3, headless, system, handover) > 1)
{
g_printerr ("Invalid option: More than one runtime mode specified\n");
return EXIT_FAILURE;
}
if (max_parallel_connections == 0)
{
max_parallel_connections = INT_MAX;
}
else if (max_parallel_connections < 0)
{
g_printerr ("Invalid number of max parallel connections: %d\n",
max_parallel_connections);
return EXIT_FAILURE;
}
if (headless)
runtime_mode = GRD_RUNTIME_MODE_HEADLESS;
else if (system)
runtime_mode = GRD_RUNTIME_MODE_SYSTEM;
else if (handover)
runtime_mode = GRD_RUNTIME_MODE_HANDOVER;
else
runtime_mode = GRD_RUNTIME_MODE_SCREEN_SHARE;
switch (runtime_mode)
{
case GRD_RUNTIME_MODE_SCREEN_SHARE:
case GRD_RUNTIME_MODE_HEADLESS:
daemon = GRD_DAEMON (grd_daemon_user_new (runtime_mode, &error));
break;
#ifdef HAVE_LIBSYSTEMD
case GRD_RUNTIME_MODE_SYSTEM:
daemon = GRD_DAEMON (grd_daemon_system_new (&error));
break;
case GRD_RUNTIME_MODE_HANDOVER:
daemon = GRD_DAEMON (grd_daemon_handover_new (&error));
break;
#else
case GRD_RUNTIME_MODE_SYSTEM:
case GRD_RUNTIME_MODE_HANDOVER:
g_assert_not_reached ();
break;
#endif /* HAVE_LIBSYSTEMD */
}
if (!daemon)
{
g_printerr ("Failed to initialize: %s\n", error->message);
return EXIT_FAILURE;
}
add_actions (G_APPLICATION (daemon));
register_signals (daemon);
context = grd_daemon_get_context (daemon);
settings = grd_context_get_settings (context);
if (rdp_port != -1)
grd_settings_override_rdp_port (settings, rdp_port);
if (vnc_port != -1)
grd_settings_override_vnc_port (settings, vnc_port);
grd_settings_override_max_parallel_connections (settings,
max_parallel_connections);
return g_application_run (G_APPLICATION (daemon), argc, argv);
}