Commit e9d21339345223698304772c60ca1175b618dd6d

Gabriel Jacobo 2013-10-13T17:15:43

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.

diff --git a/README-raspberrypi.txt b/README-raspberrypi.txt
index 744fd89..0724a8c 100644
--- a/README-raspberrypi.txt
+++ b/README-raspberrypi.txt
@@ -148,5 +148,8 @@ this determining the CAPS LOCK behavior:
  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.
+  
 
diff --git a/src/input/evdev/SDL_evdev.c b/src/input/evdev/SDL_evdev.c
index 497cd40..92dc4fe 100644
--- a/src/input/evdev/SDL_evdev.c
+++ b/src/input/evdev/SDL_evdev.c
@@ -45,6 +45,18 @@ static _THIS = NULL;
 #include <linux/keyboard.h>
 #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_assert.h"
 #include "SDL_endian.h"
@@ -370,6 +382,72 @@ static int SDL_EVDEV_get_console_fd(void)
     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
 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 */
         _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;
@@ -429,6 +520,12 @@ SDL_EVDEV_Quit(void)
         if (_this->console_fd >= 0) {
             close(_this->console_fd);
         }
+        
+        if (_this->tty >= 0) {
+            SDL_EVDEV_unmute_keyboard(_this->tty, _this->kb_mode);
+            close(_this->tty);
+        }
+        
         /* Remove existing devices */
         while(_this->first != NULL) {
             SDL_EVDEV_device_removed(_this->first->path);
diff --git a/src/input/evdev/SDL_evdev.h b/src/input/evdev/SDL_evdev.h
index 34f369d..b9311d4 100644
--- a/src/input/evdev/SDL_evdev.h
+++ b/src/input/evdev/SDL_evdev.h
@@ -43,6 +43,8 @@ typedef struct SDL_EVDEV_PrivateData
     int numdevices;
     int ref_count;
     int console_fd;
+    int kb_mode;
+    int tty;
 } SDL_EVDEV_PrivateData;
 
 extern int SDL_EVDEV_Init(void);