Prevent keystrokes from leaking through to the console when using evdev.

This uses the same method Weston and X use. Sadly, to be fully effective when
launching remotely, this needs root permissions.
This commit is contained in:
Gabriel Jacobo 2013-10-13 17:15:43 -03:00
parent 074a1c4c63
commit e9d2133934
3 changed files with 103 additions and 1 deletions

View file

@ -148,5 +148,8 @@ this determining the CAPS LOCK behavior:
Notes Notes
================================================================================ ================================================================================
* Input events from the keyboard leak through to the console * When launching apps remotely (via SSH), SDL can prevent local keystrokes from
leaking into the console only if it has root privileges. Launching apps locally
does not suffer from this issue.

View file

@ -45,6 +45,18 @@ static _THIS = NULL;
#include <linux/keyboard.h> #include <linux/keyboard.h>
#endif #endif
/* We need this to prevent keystrokes from appear in the console */
#ifndef KDSKBMUTE
#define KDSKBMUTE 0x4B51
#endif
#ifndef KDSKBMODE
#define KDSKBMODE 0x4B45
#endif
#ifndef K_OFF
#define K_OFF 0x04
#endif
#include "SDL.h" #include "SDL.h"
#include "SDL_assert.h" #include "SDL_assert.h"
#include "SDL_endian.h" #include "SDL_endian.h"
@ -370,6 +382,72 @@ static int SDL_EVDEV_get_console_fd(void)
return -1; return -1;
} }
/* Prevent keystrokes from reaching the tty */
static int SDL_EVDEV_mute_keyboard(int tty, int *kb_mode)
{
char arg;
*kb_mode = 0; /* FIXME: Is this a sane default in case KDGKBMODE fails? */
if (!IS_CONSOLE(tty)) {
return SDL_SetError("Tried to mute an invalid tty");
}
ioctl(tty, KDGKBMODE, kb_mode); /* It's not fatal if this fails */
if (ioctl(tty, KDSKBMUTE, 1) && ioctl(tty, KDSKBMODE, K_OFF)) {
return SDL_SetError("EVDEV: Failed muting keyboard");
}
return 0;
}
/* Restore the keyboard mode for given tty */
static void SDL_EVDEV_unmute_keyboard(int tty, int kb_mode)
{
if (ioctl(tty, KDSKBMUTE, 0) && ioctl(tty, KDSKBMODE, kb_mode)) {
SDL_Log("EVDEV: Failed restoring keyboard mode");
}
}
/* Read /sys/class/tty/tty0/active and open the tty */
static int SDL_EVDEV_get_active_tty()
{
int fd, len;
char ttyname[NAME_MAX + 1];
char ttypath[PATH_MAX+1] = "/dev/";
char arg;
fd = open("/sys/class/tty/tty0/active", O_RDONLY);
if (fd < 0) {
return SDL_SetError("Could not determine which tty is active");
}
len = read(fd, ttyname, NAME_MAX);
close(fd);
if (len <= 0) {
return SDL_SetError("Could not read which tty is active");
}
if (ttyname[len-1] == '\n') {
ttyname[len-1] = '\0';
}
else {
ttyname[len] = '\0';
}
SDL_strlcat(ttypath, ttyname, PATH_MAX);
fd = open(ttypath, O_RDWR | O_NOCTTY);
if (fd < 0) {
return SDL_SetError("Could not open tty: %s", ttypath);
}
if (!IS_CONSOLE(fd)) {
close(fd);
return SDL_SetError("Invalid tty obtained: %s", ttypath);
}
return fd;
}
int int
SDL_EVDEV_Init(void) SDL_EVDEV_Init(void)
{ {
@ -403,6 +481,19 @@ SDL_EVDEV_Init(void)
/* We need a physical terminal (not PTS) to be able to translate key code to symbols via the kernel tables */ /* We need a physical terminal (not PTS) to be able to translate key code to symbols via the kernel tables */
_this->console_fd = SDL_EVDEV_get_console_fd(); _this->console_fd = SDL_EVDEV_get_console_fd();
/* Mute the keyboard so keystrokes only generate evdev events and do not leak through to the console */
_this->tty = STDIN_FILENO;
if (SDL_EVDEV_mute_keyboard(_this->tty, &_this->kb_mode) < 0) {
/* stdin is not a tty, probably we were launched remotely, so we try to disable the active tty */
_this->tty = SDL_EVDEV_get_active_tty();
if (_this->tty >= 0) {
if (SDL_EVDEV_mute_keyboard(_this->tty, &_this->kb_mode) < 0) {
close(_this->tty);
_this->tty = -1;
}
}
}
} }
_this->ref_count += 1; _this->ref_count += 1;
@ -429,6 +520,12 @@ SDL_EVDEV_Quit(void)
if (_this->console_fd >= 0) { if (_this->console_fd >= 0) {
close(_this->console_fd); close(_this->console_fd);
} }
if (_this->tty >= 0) {
SDL_EVDEV_unmute_keyboard(_this->tty, _this->kb_mode);
close(_this->tty);
}
/* Remove existing devices */ /* Remove existing devices */
while(_this->first != NULL) { while(_this->first != NULL) {
SDL_EVDEV_device_removed(_this->first->path); SDL_EVDEV_device_removed(_this->first->path);

View file

@ -43,6 +43,8 @@ typedef struct SDL_EVDEV_PrivateData
int numdevices; int numdevices;
int ref_count; int ref_count;
int console_fd; int console_fd;
int kb_mode;
int tty;
} SDL_EVDEV_PrivateData; } SDL_EVDEV_PrivateData;
extern int SDL_EVDEV_Init(void); extern int SDL_EVDEV_Init(void);