/* * 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 */ #include "config.h" #include "grd-daemon.h" #include #include #include #include #include #include #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); }