From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Victor Toso Date: Sat, 9 Apr 2016 12:00:08 +0200 Subject: [PATCH] session-info: check for a locked session Each session back-end can return this information to vdagentd when requested. The agent should use this on situations that should not work when session is locked such as file-transfer-start which is fixed by this patch. systemd-login is the only back-end implementing this function at the moment and I'll address console-kit back-end in a later patch. Also, this patch makes spice-vdagent depend on dbus for getting the lock information. Resolve: https://bugzilla.redhat.com/show_bug.cgi?id=1323623 Acked-by: Jonathon Jongsma (cherry picked from commit 364b6bba068bd694d7c4355b6275f88482d9f3f8) --- configure.ac | 17 ++----- src/console-kit.c | 7 +++ src/dummy-session-info.c | 5 ++ src/session-info.h | 3 ++ src/systemd-login.c | 127 +++++++++++++++++++++++++++++++++++++++++++++++ src/vdagentd.c | 7 +++ 6 files changed, 153 insertions(+), 13 deletions(-) diff --git a/configure.ac b/configure.ac index 9f9d34c..9903ea9 100644 --- a/configure.ac +++ b/configure.ac @@ -81,6 +81,7 @@ PKG_CHECK_MODULES([GLIB2], [glib-2.0 >= 2.12]) PKG_CHECK_MODULES(X, [xfixes xrandr >= 1.3 xinerama x11]) PKG_CHECK_MODULES(SPICE, [spice-protocol >= 0.12.5]) PKG_CHECK_MODULES(ALSA, [alsa >= 1.0.22]) +PKG_CHECK_MODULES([DBUS], [dbus-1]) if test "$with_session_info" = "auto" || test "$with_session_info" = "systemd"; then PKG_CHECK_MODULES([LIBSYSTEMD_LOGIN], @@ -100,19 +101,9 @@ fi AM_CONDITIONAL(HAVE_LIBSYSTEMD_LOGIN, test x"$have_libsystemd_login" = "xyes") if test "$with_session_info" = "auto" || test "$with_session_info" = "console-kit"; then - PKG_CHECK_MODULES([DBUS], - [dbus-1], - [have_console_kit="yes"], - [have_console_kit="no"]) - if test x"$have_console_kit" = "xno" && test "$with_session_info" = "console-kit"; then - AC_MSG_ERROR([console-kit support explicitly requested, but some required packages are not available]) - fi - if test x"$have_console_kit" = "xyes"; then - AC_DEFINE([HAVE_CONSOLE_KIT], [1], [If defined, vdagentd will be compiled with ConsoleKit support]) - with_session_info="console-kit" - else - with_session_info="none" - fi + AC_DEFINE([HAVE_CONSOLE_KIT], [1], [If defined, vdagentd will be compiled with ConsoleKit support]) + have_console_kit="yes" + with_session_info="console-kit" else have_console_kit="no" fi diff --git a/src/console-kit.c b/src/console-kit.c index 759a81e..260fcc7 100644 --- a/src/console-kit.c +++ b/src/console-kit.c @@ -352,3 +352,10 @@ static char *console_kit_check_active_session_change(struct session_info *ck) return ck->active_session; } + +gboolean session_info_session_is_locked(struct session_info *info) +{ + /* TODO: It could be implemented based on Lock/Unlock signals from Session + * interface. */ + return FALSE; +} diff --git a/src/dummy-session-info.c b/src/dummy-session-info.c index e188ddc..c09643b 100644 --- a/src/dummy-session-info.c +++ b/src/dummy-session-info.c @@ -44,3 +44,8 @@ char *session_info_session_for_pid(struct session_info *si, uint32_t pid) { return NULL; } + +gboolean session_is_locked(struct session_info *ck) +{ + return FALSE; +} diff --git a/src/session-info.h b/src/session-info.h index 67099de..d660fcf 100644 --- a/src/session-info.h +++ b/src/session-info.h @@ -24,6 +24,7 @@ #include #include +#include struct session_info; @@ -36,4 +37,6 @@ const char *session_info_get_active_session(struct session_info *ck); /* Note result must be free()-ed by caller */ char *session_info_session_for_pid(struct session_info *ck, uint32_t pid); +gboolean session_info_session_is_locked(struct session_info *si); + #endif diff --git a/src/systemd-login.c b/src/systemd-login.c index 73db37f..4a365c0 100644 --- a/src/systemd-login.c +++ b/src/systemd-login.c @@ -25,13 +25,121 @@ #include #include #include +#include struct session_info { int verbose; sd_login_monitor *mon; char *session; + struct { + DBusConnection *system_connection; + char *match_session_signals; + } dbus; + gboolean session_is_locked; }; +#define LOGIND_SESSION_INTERFACE "org.freedesktop.login1.Session" +#define LOGIND_SESSION_OBJ_TEMPLATE "'/org/freedesktop/login1/session/_3%s'" + +#define SESSION_SIGNAL_LOCK "Lock" +#define SESSION_SIGNAL_UNLOCK "Unlock" + +/* dbus related */ +static DBusConnection *si_dbus_get_system_bus(void) +{ + DBusConnection *connection; + DBusError error; + + dbus_error_init(&error); + connection = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); + if (connection == NULL || dbus_error_is_set(&error)) { + if (dbus_error_is_set(&error)) { + syslog(LOG_WARNING, "Unable to connect to system bus: %s", + error.message); + dbus_error_free(&error); + } else { + syslog(LOG_WARNING, "Unable to connect to system bus"); + } + return NULL; + } + return connection; +} + +static void si_dbus_match_remove(struct session_info *si) +{ + DBusError error; + if (si->dbus.match_session_signals == NULL) + return; + + dbus_error_init(&error); + dbus_bus_remove_match(si->dbus.system_connection, + si->dbus.match_session_signals, + &error); + + g_free(si->dbus.match_session_signals); + si->dbus.match_session_signals = NULL; +} + +static void si_dbus_match_rule_update(struct session_info *si) +{ + DBusError error; + + if (si->dbus.system_connection == NULL || + si->session == NULL) + return; + + si_dbus_match_remove(si); + + si->dbus.match_session_signals = + g_strdup_printf ("type='signal',interface='%s',path=" + LOGIND_SESSION_OBJ_TEMPLATE, + LOGIND_SESSION_INTERFACE, + si->session); + if (si->verbose) + syslog(LOG_DEBUG, "logind match: %s", si->dbus.match_session_signals); + + dbus_error_init(&error); + dbus_bus_add_match(si->dbus.system_connection, + si->dbus.match_session_signals, + &error); + if (dbus_error_is_set(&error)) { + syslog(LOG_WARNING, "Unable to add dbus rule match: %s", + error.message); + dbus_error_free(&error); + g_free(si->dbus.match_session_signals); + si->dbus.match_session_signals = NULL; + } +} + +static void +si_dbus_read_signals(struct session_info *si) +{ + DBusMessage *message = NULL; + + dbus_connection_read_write(si->dbus.system_connection, 0); + message = dbus_connection_pop_message(si->dbus.system_connection); + while (message != NULL) { + const char *member; + + member = dbus_message_get_member (message); + if (g_strcmp0(member, SESSION_SIGNAL_LOCK) == 0) { + si->session_is_locked = TRUE; + } else if (g_strcmp0(member, SESSION_SIGNAL_UNLOCK) == 0) { + si->session_is_locked = FALSE; + } else { + if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL) { + syslog(LOG_WARNING, "(systemd-login) received non signal message"); + } else if (si->verbose) { + syslog(LOG_DEBUG, "(systemd-login) Signal not handled: %s", member); + } + } + + dbus_message_unref(message); + dbus_connection_read_write(si->dbus.system_connection, 0); + message = dbus_connection_pop_message(si->dbus.system_connection); + } +} + struct session_info *session_info_create(int verbose) { struct session_info *si; @@ -42,6 +150,7 @@ struct session_info *session_info_create(int verbose) return NULL; si->verbose = verbose; + si->session_is_locked = FALSE; r = sd_login_monitor_new("session", &si->mon); if (r < 0) { @@ -50,6 +159,7 @@ struct session_info *session_info_create(int verbose) return NULL; } + si->dbus.system_connection = si_dbus_get_system_bus(); return si; } @@ -58,6 +168,8 @@ void session_info_destroy(struct session_info *si) if (!si) return; + si_dbus_match_remove(si); + dbus_connection_close(si->dbus.system_connection); sd_login_monitor_unref(si->mon); free(si->session); free(si); @@ -87,6 +199,7 @@ const char *session_info_get_active_session(struct session_info *si) sd_login_monitor_flush(si->mon); free(old_session); + si_dbus_match_rule_update(si); return si->session; } @@ -104,3 +217,17 @@ char *session_info_session_for_pid(struct session_info *si, uint32_t pid) return session; } + +gboolean session_info_session_is_locked(struct session_info *si) +{ + g_return_val_if_fail (si != NULL, FALSE); + + /* We could also rely on IdleHint property from Session which seems to work + * well in rhel7 but it wasn't working well in my own system (F23). I'm + * convinced for now that Lock/Unlock signals should be enough but that + * means Lock/Unlock being done by logind. That might take a while. + * Check: https://bugzilla.gnome.org/show_bug.cgi?id=764773 */ + + si_dbus_read_signals(si); + return si->session_is_locked; +} diff --git a/src/vdagentd.c b/src/vdagentd.c index 2c8e973..2f77773 100644 --- a/src/vdagentd.c +++ b/src/vdagentd.c @@ -276,6 +276,13 @@ static void do_client_file_xfer(struct vdagent_virtio_port *vport, "active session, cancelling client file-xfer request %u", s->id, VD_AGENT_FILE_XFER_STATUS_CANCELLED); return; + } else if (session_info_session_is_locked(session_info)) { + syslog(LOG_DEBUG, "Session is locked, skipping file-xfer-start"); + send_file_xfer_status(vport, + "User's session is locked and cannot start file transfer. " + "Cancelling client file-xfer request %u", + s->id, VD_AGENT_FILE_XFER_STATUS_ERROR); + return; } udscs_write(active_session_conn, VDAGENTD_FILE_XFER_START, 0, 0, data, message_header->size);