Browse Source

Add keyboard hotplug (add/remove) support to tsak

This closes Bug 587
Fix warning in kompmgr
tags/r14.0.0
Timothy Pearson 8 years ago
parent
commit
5f413b26eb
3 changed files with 350 additions and 116 deletions
  1. +1
    -0
      tsak/CMakeLists.txt
  2. +347
    -115
      tsak/main.cpp
  3. +2
    -1
      twin/kompmgr/kompmgr.c

+ 1
- 0
tsak/CMakeLists.txt View File

@@ -23,5 +23,6 @@ link_directories(

tde_add_executable( tsak
SOURCES main.cpp
LINK udev
DESTINATION ${BIN_INSTALL_DIR}
)

+ 347
- 115
tsak/main.cpp View File

@@ -1,8 +1,8 @@
/*
Copyright 2010 Adam Marchetti
Copyright 2011 Timothy Pearson <kb9vqf@pearsoncomputing.net>
Copyright 2011-2012 Timothy Pearson <kb9vqf@pearsoncomputing.net>

This file is part of tsak.
This file is part of tsak, the TDE Secure Attention Key daemon

tsak is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as
@@ -35,9 +35,15 @@ License along with tsak. If not, see http://www.gnu.org/licenses/.
#include <sys/time.h>
#include <termios.h>
#include <signal.h>
#include <libudev.h>
#include <libgen.h>

#define FIFO_DIR "/tmp/ksocket-global"
#define FIFO_FILE_OUT "/tmp/ksocket-global/tsak"
#define FIFO_LOCKFILE_OUT "/tmp/ksocket-global/tsak.lock"

#define MAX_KEYBOARDS 64
#define MAX_INPUT_NODE 128

#define TestBit(bit, array) (array[(bit) / 8] & (1 << ((bit) % 8)))

@@ -46,9 +52,18 @@ typedef unsigned char byte;
bool mPipeOpen_out = false;
int mPipe_fd_out = -1;

int mPipe_lockfd_out = -1;

char filename[32];
char key_bitmask[(KEY_MAX + 7) / 8];

struct sigaction usr_action;
sigset_t block_mask;

int keyboard_fd_num;
int keyboard_fds[MAX_KEYBOARDS];
int child_pids[MAX_KEYBOARDS];

const char *keycode[256] =
{
"", "<esc>", "1", "2", "3", "4", "5", "6", "7", "8",
@@ -79,6 +94,26 @@ int bit_set(size_t i, const byte* a)
return a[i/CHAR_BIT] & (1 << i%CHAR_BIT);
}

// --------------------------------------------------------------------------------------
// Useful function from Stack Overflow
// http://stackoverflow.com/questions/874134/find-if-string-endswith-another-string-in-c
// --------------------------------------------------------------------------------------
/* returns 1 iff str ends with suffix */
int str_ends_with(const char * str, const char * suffix) {

if( str == NULL || suffix == NULL )
return 0;

size_t str_len = strlen(str);
size_t suffix_len = strlen(suffix);

if(suffix_len > str_len)
return 0;

return 0 == strncmp( str + str_len - suffix_len, suffix, suffix_len );
}
// --------------------------------------------------------------------------------------

/* Assign features (supported axes and keys) of the physical input device (devin)
* to the virtual input device (devout) */
static void copy_features(int devin, int devout)
@@ -111,26 +146,40 @@ static void copy_features(int devin, int devout)
}
}

int find_keyboard() {
int find_keyboards() {
int i, j;
int fd;
char filename[32];
char key_bitmask[(KEY_MAX + 7) / 8];
char name[256] = "Unknown";

keyboard_fd_num = 0;
for (i=0; i<MAX_KEYBOARDS; i++) {
keyboard_fds[i] = 0;
}
for (i=0; i<32; i++) {
for (i=0; i<MAX_INPUT_NODE; i++) {
snprintf(filename,sizeof(filename), "/dev/input/event%d", i);
fd = open(filename, O_RDWR|O_SYNC);
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask);
/* We assume that anything that has an alphabetic key in the
QWERTYUIOP range in it is the main keyboard. */
for (j = KEY_Q; j <= KEY_P; j++) {
if (TestBit(j, key_bitmask))
return fd;

// Ensure that we do not detect our own tsak faked keyboards
ioctl (fd, EVIOCGNAME (sizeof (name)), name);
if (str_ends_with(name, "+tsak") == 0) {
/* We assume that anything that has an alphabetic key in the
QWERTYUIOP range in it is the main keyboard. */
for (j = KEY_Q; j <= KEY_P; j++) {
if (TestBit(j, key_bitmask)) {
keyboard_fds[keyboard_fd_num] = fd;
}
}
}

if (keyboard_fds[keyboard_fd_num] == 0) {
close (fd);
}
else {
keyboard_fd_num++;
}
close (fd);
}
return 0;
}
@@ -144,6 +193,12 @@ void tearDownPipe()
}
}

void tearDownLockingPipe()
{
close(mPipe_lockfd_out);
unlink(FIFO_LOCKFILE_OUT);
}

bool setFileLock(int fd, bool close_on_failure)
{
struct flock fl;
@@ -154,8 +209,8 @@ bool setFileLock(int fd, bool close_on_failure)
fl.l_len = 1;

// Set the exclusive file lock
if (fcntl(mPipe_fd_out, F_SETLK, &fl) == -1) {
close(mPipe_fd_out);
if (fcntl(fd, F_SETLK, &fl) == -1) {
close(fd);
return false;
}

@@ -171,7 +226,7 @@ bool checkFileLock()
fl.l_whence = SEEK_SET;
fl.l_len = 0;

int fd = open(FIFO_FILE_OUT, O_RDWR | O_NONBLOCK);
int fd = open(FIFO_LOCKFILE_OUT, O_RDWR | O_NONBLOCK);
fcntl(fd, F_GETLK, &fl); /* Overwrites lock structure with preventors. */

if (fd > -1) {
@@ -202,6 +257,71 @@ bool setupPipe()
return setFileLock(mPipe_fd_out, true);
}

bool setupLockingPipe()
{
/* Create the FIFOs if they do not exist */
umask(0);
mkdir(FIFO_DIR,0644);
mknod(FIFO_LOCKFILE_OUT, S_IFIFO|0600, 0);
chmod(FIFO_LOCKFILE_OUT, 0600);
mPipe_lockfd_out = open(FIFO_LOCKFILE_OUT, O_RDWR | O_NONBLOCK);
if (mPipe_lockfd_out > -1) {
// Set the exclusive file lock
return setFileLock(mPipe_lockfd_out, true);
}

return false;
}

void broadcast_sak()
{
// Let anyone listening to our interface know that an SAK keypress was received
// I highly doubt there are more than 255 VTs active at once...
int i;
for (i=0;i<255;i++) {
write(mPipe_fd_out, "SAK\n\r", 6);
}
}

void restart_tsak()
{
int i;

fprintf(stderr, "Forcibly terminating...\n");

// Close down all child processes
for (i=0; i<MAX_KEYBOARDS; i++) {
if (child_pids[i] != 0) {
kill(child_pids[i], SIGKILL);
}
}

// Wait for process termination
sleep(1);

// Release all exclusive keyboard locks
for (int current_keyboard=0;current_keyboard<keyboard_fd_num;current_keyboard++) {
if(ioctl(keyboard_fds[current_keyboard], EVIOCGRAB, 0) < 0) {
fprintf(stderr, "Failed to release exclusive input device lock");
}
close(keyboard_fds[current_keyboard]);
}

#if 1
// Restart now
// Note that the execl function never returns
char me[2048];
int chars = readlink("/proc/self/exe", me, sizeof(me));
me[chars] = 0;
me[2047] = 0;
execl(me, basename(me), (char*)NULL);
#else
_exit(0);
#endif
}

class PipeHandler
{
public:
@@ -215,7 +335,7 @@ PipeHandler::PipeHandler()

PipeHandler::~PipeHandler()
{
tearDownPipe();
tearDownLockingPipe();
}

int main (int argc, char *argv[])
@@ -223,13 +343,19 @@ int main (int argc, char *argv[])
struct input_event ev[64];
struct input_event event;
struct uinput_user_dev devinfo={0};
int fd, devout, rd, value, size = sizeof (struct input_event);
int devout[MAX_KEYBOARDS], rd, i, value, size = sizeof (struct input_event);
char name[256] = "Unknown";
bool ctrl_down = false;
bool alt_down = false;
bool hide_event = false;
bool established = false;
bool testrun = false;
int current_keyboard;
bool can_proceed;

for (i=0; i<MAX_KEYBOARDS; i++) {
child_pids[i] = 0;
}

if (argc == 2) {
if (strcmp(argv[1], "checkactive") == 0) {
@@ -239,7 +365,11 @@ int main (int argc, char *argv[])

// Check for existing file locks
if (!checkFileLock()) {
fprintf(stderr, "Another instance of this program is already running\n");
fprintf(stderr, "Another instance of this program is already running [1]\n");
return 8;
}
if (!setupLockingPipe()) {
fprintf(stderr, "Another instance of this program is already running [2]\n");
return 8;
}

@@ -256,125 +386,227 @@ int main (int argc, char *argv[])
return 5;
}
// Open Device
fd = find_keyboard();
if (fd == -1) {
printf ("Could not find your keyboard!\n");
// Find keyboards
find_keyboards();
if (keyboard_fd_num == 0) {
printf ("Could not find any usable keyboard(s)!\n");
// Make sure everyone knows we physically can't detect a SAK
// Before we do this we broadcast one so that active dialogs are updated appropriately
// Also, we keep watching for a keyboard to be added via a forked child process...
broadcast_sak();
if (established)
sleep(1);
else
return 4;
else {
int i=fork();
if (i<0) return 12; // fork failed
if (i>0) {
return 4;
}
sleep(1);
restart_tsak();
}
}
else {
// Print Device Name
ioctl (fd, EVIOCGNAME (sizeof (name)), name);
fprintf(stderr, "Reading From : (%s)\n", name);
// Create filtered virtual output device
devout=open("/dev/misc/uinput",O_WRONLY|O_NONBLOCK);
if (devout<0) {
perror("open(\"/dev/misc/uinput\")");
devout=open("/dev/uinput",O_WRONLY|O_NONBLOCK);
}
if (devout<0) {
fprintf(stderr,"Unable to open /dev/uinput or /dev/misc/uinput (char device 10:223).\nPossible causes:\n 1) Device node does not exist\n 2) Kernel not compiled with evdev [INPUT_EVDEV] and uinput [INPUT_UINPUT] user level driver support\n 3) Permission denied.\n");
perror("open(\"/dev/uinput\")");
if (established)
sleep(1);
else
return 3;
}
else {
if(ioctl(fd, EVIOCGRAB, 2) < 0) {
close(fd);
fprintf(stderr, "Failed to grab exclusive input device lock");
fprintf(stderr, "Found %d keyboard(s)\n", keyboard_fd_num);

can_proceed = true;
for (current_keyboard=0;current_keyboard<keyboard_fd_num;current_keyboard++) {
// Print Device Name
ioctl (keyboard_fds[current_keyboard], EVIOCGNAME (sizeof (name)), name);
fprintf(stderr, "Reading from keyboard: (%s)\n", name);
// Create filtered virtual output device
devout[current_keyboard]=open("/dev/misc/uinput",O_WRONLY|O_NONBLOCK);
if (devout[current_keyboard]<0) {
devout[current_keyboard]=open("/dev/uinput",O_WRONLY|O_NONBLOCK);
if (devout[current_keyboard]<0) {
perror("open(\"/dev/misc/uinput\")");
}
}
if (devout[current_keyboard]<0) {
can_proceed = false;
fprintf(stderr, "Unable to open /dev/uinput or /dev/misc/uinput (char device 10:223).\nPossible causes:\n 1) Device node does not exist\n 2) Kernel not compiled with evdev [INPUT_EVDEV] and uinput [INPUT_UINPUT] user level driver support\n 3) Permission denied.\n");
perror("open(\"/dev/uinput\")");
if (established)
sleep(1);
else
return 1;
return 3;
}
else {
ioctl(fd, EVIOCGNAME(UINPUT_MAX_NAME_SIZE), devinfo.name);
strncat(devinfo.name, "+tsak", UINPUT_MAX_NAME_SIZE-1);
fprintf(stderr, "%s\n", devinfo.name);
ioctl(fd, EVIOCGID, &devinfo.id);
copy_features(fd, devout);
write(devout,&devinfo,sizeof(devinfo));
if (ioctl(devout,UI_DEV_CREATE)<0) {
fprintf(stderr,"Unable to create input device with UI_DEV_CREATE\n");
}

if (can_proceed == true) {
for (current_keyboard=0;current_keyboard<keyboard_fd_num;current_keyboard++) {
if(ioctl(keyboard_fds[current_keyboard], EVIOCGRAB, 2) < 0) {
close(keyboard_fds[current_keyboard]);
fprintf(stderr, "Failed to grab exclusive input device lock");
if (established)
sleep(1);
else
return 2;
return 1;
}
else {
fprintf(stderr,"Device created.\n");

if (established == false) {
tearDownPipe();
int i=fork();
if (i<0) return 9; // fork failed
if (i>0) {
// close parent process
close(mPipe_fd_out);
return 0;
}
setupPipe();
ioctl(keyboard_fds[current_keyboard], EVIOCGNAME(UINPUT_MAX_NAME_SIZE), devinfo.name);
strncat(devinfo.name, "+tsak", UINPUT_MAX_NAME_SIZE-1);
fprintf(stderr, "%s\n", devinfo.name);
ioctl(keyboard_fds[current_keyboard], EVIOCGID, &devinfo.id);
copy_features(keyboard_fds[current_keyboard], devout[current_keyboard]);
write(devout[current_keyboard],&devinfo,sizeof(devinfo));
if (ioctl(devout[current_keyboard],UI_DEV_CREATE)<0) {
fprintf(stderr, "Unable to create input device with UI_DEV_CREATE\n");
if (established)
sleep(1);
else
return 2;
}

established = true;

if (testrun == true) {
return 0;
}
while (1) {
if ((rd = read (fd, ev, size * 2)) < size) {
fprintf(stderr,"Read failed.\n");
break;
}
value = ev[0].value;
if (value != ' ' && ev[1].value == 0 && ev[1].type == 1){ // Read the key release event
if (keycode[(ev[1].code)]) {
if (strcmp(keycode[(ev[1].code)], "<control>") == 0) ctrl_down = false;
if (strcmp(keycode[(ev[1].code)], "<alt>") == 0) alt_down = false;
else {
fprintf(stderr, "Device created.\n");
if (established == false) {
int i=fork();
if (i<0) return 9; // fork failed
if (i>0) {
child_pids[current_keyboard] = i;
continue;
}
setupLockingPipe();
}
if (value != ' ' && ev[1].value == 1 && ev[1].type == 1){ // Read the key press event
if (keycode[(ev[1].code)]) {
if (strcmp(keycode[(ev[1].code)], "<control>") == 0) ctrl_down = true;
if (strcmp(keycode[(ev[1].code)], "<alt>") == 0) alt_down = true;
}
established = true;
if (testrun == true) {
return 0;
}
hide_event = false;
if (keycode[(ev[1].code)]) {
if (alt_down && ctrl_down && (strcmp(keycode[(ev[1].code)], "<del>") == 0)) {
hide_event = true;
while (1) {
if ((rd = read (keyboard_fds[current_keyboard], ev, size * 2)) < size) {
fprintf(stderr, "Read failed.\n");
break;
}
value = ev[0].value;
if (value != ' ' && ev[1].value == 0 && ev[1].type == 1){ // Read the key release event
if (keycode[(ev[1].code)]) {
if (strcmp(keycode[(ev[1].code)], "<control>") == 0) ctrl_down = false;
if (strcmp(keycode[(ev[1].code)], "<alt>") == 0) alt_down = false;
}
}
if (value != ' ' && ev[1].value == 1 && ev[1].type == 1){ // Read the key press event
if (keycode[(ev[1].code)]) {
if (strcmp(keycode[(ev[1].code)], "<control>") == 0) ctrl_down = true;
if (strcmp(keycode[(ev[1].code)], "<alt>") == 0) alt_down = true;
}
}
hide_event = false;
if (keycode[(ev[1].code)]) {
if (alt_down && ctrl_down && (strcmp(keycode[(ev[1].code)], "<del>") == 0)) {
hide_event = true;
}
}
if (hide_event == false) {
// Pass the event on...
event = ev[0];
write(devout[current_keyboard], &event, sizeof event);
event = ev[1];
write(devout[current_keyboard], &event, sizeof event);
}
if (hide_event == true) {
// Let anyone listening to our interface know that an SAK keypress was received
broadcast_sak();
}
}
}
}
}

// fork udev monitor process
int i=fork();
if (i<0) return 10; // fork failed
if (i>0) {
// Terminate parent
return 0;
}

// Prevent multiple process instances from starting
setupLockingPipe();

// Wait a little bit so that udev hotplug can stabilize before we start monitoring
sleep(1);

fprintf(stderr, "Hotplug monitoring process started\n");

// Monitor for hotplugged keyboards
int j;
int hotplug_fd;
bool is_new_keyboard;
struct udev *udev;
struct udev_device *dev;
struct udev_monitor *mon;

// Create the udev object
udev = udev_new();
if (!udev) {
fprintf(stderr, "Cannot connect to udev interface\n");
return 11;
}

// Set up a udev monitor to monitor input devices
mon = udev_monitor_new_from_netlink(udev, "udev");
udev_monitor_filter_add_match_subsystem_devtype(mon, "input", NULL);
udev_monitor_enable_receiving(mon);
while (1) {
// Watch for input from the monitoring process
dev = udev_monitor_receive_device(mon);
if (dev) {
// If a keyboard was removed we need to restart...
if (strcmp(udev_device_get_action(dev), "remove") == 0) {
udev_device_unref(dev);
udev_unref(udev);
restart_tsak();
}

is_new_keyboard = false;
snprintf(filename,sizeof(filename), "%s", udev_device_get_devnode(dev));
udev_device_unref(dev);

// Print name of keyboard
hotplug_fd = open(filename, O_RDWR|O_SYNC);
ioctl(hotplug_fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask);
if (hide_event == false) {
// Pass the event on...
event = ev[0];
write(devout, &event, sizeof event);
event = ev[1];
write(devout, &event, sizeof event);
}
if (hide_event == true) {
// Let anyone listening to our interface know that an SAK keypress was received
// I highly doubt there are more than 255 VTs active at once...
int i;
for (i=0;i<255;i++) {
write(mPipe_fd_out, "SAK\n\r", 6);
}
/* We assume that anything that has an alphabetic key in the
QWERTYUIOP range in it is the main keyboard. */
for (j = KEY_Q; j <= KEY_P; j++) {
if (TestBit(j, key_bitmask)) {
is_new_keyboard = true;
}
}
ioctl (hotplug_fd, EVIOCGNAME (sizeof (name)), name);
close(hotplug_fd);

// Ensure that we do not detect our own tsak faked keyboards
if (str_ends_with(name, "+tsak") == 1) {
is_new_keyboard = false;
}

// If a keyboard was added we need to restart...
if (is_new_keyboard == true) {
fprintf(stderr, "Hotplugged new keyboard: (%s)\n", name);
udev_unref(udev);
restart_tsak();
}
}
else {
fprintf(stderr, "No Device from receive_device(). An error occured.\n");
}
}

udev_unref(udev);

fprintf(stderr, "Hotplug monitoring process terminated\n");
}
}
}


+ 2
- 1
twin/kompmgr/kompmgr.c View File

@@ -60,6 +60,7 @@ check baghira.sf.net for more infos
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <libgen.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
@@ -397,7 +398,7 @@ void delete_pid_file()
int chars = readlink("/proc/self/exe", me, sizeof(me));
me[chars] = 0;
me[2047] = 0;
execl(me, NULL);
execl(me, basename(me), (char*)NULL);
}
#endif
}


Loading…
Cancel
Save