Add keyboard hotplug (add/remove) support to tsak

This closes Bug 587
Fix warning in kompmgr
pull/2/head
Timothy Pearson 12 years ago
parent 1e2983ad01
commit 5f413b26eb

@ -23,5 +23,6 @@ link_directories(
tde_add_executable( tsak tde_add_executable( tsak
SOURCES main.cpp SOURCES main.cpp
LINK udev
DESTINATION ${BIN_INSTALL_DIR} DESTINATION ${BIN_INSTALL_DIR}
) )

@ -1,8 +1,8 @@
/* /*
Copyright 2010 Adam Marchetti 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 tsak is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as 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 <sys/time.h>
#include <termios.h> #include <termios.h>
#include <signal.h> #include <signal.h>
#include <libudev.h>
#include <libgen.h>
#define FIFO_DIR "/tmp/ksocket-global" #define FIFO_DIR "/tmp/ksocket-global"
#define FIFO_FILE_OUT "/tmp/ksocket-global/tsak" #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))) #define TestBit(bit, array) (array[(bit) / 8] & (1 << ((bit) % 8)))
@ -46,9 +52,18 @@ typedef unsigned char byte;
bool mPipeOpen_out = false; bool mPipeOpen_out = false;
int mPipe_fd_out = -1; int mPipe_fd_out = -1;
int mPipe_lockfd_out = -1;
char filename[32];
char key_bitmask[(KEY_MAX + 7) / 8];
struct sigaction usr_action; struct sigaction usr_action;
sigset_t block_mask; sigset_t block_mask;
int keyboard_fd_num;
int keyboard_fds[MAX_KEYBOARDS];
int child_pids[MAX_KEYBOARDS];
const char *keycode[256] = const char *keycode[256] =
{ {
"", "<esc>", "1", "2", "3", "4", "5", "6", "7", "8", "", "<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); 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) /* Assign features (supported axes and keys) of the physical input device (devin)
* to the virtual input device (devout) */ * to the virtual input device (devout) */
static void copy_features(int devin, int 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 i, j;
int fd; int fd;
char filename[32]; char name[256] = "Unknown";
char key_bitmask[(KEY_MAX + 7) / 8];
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); snprintf(filename,sizeof(filename), "/dev/input/event%d", i);
fd = open(filename, O_RDWR|O_SYNC); fd = open(filename, O_RDWR|O_SYNC);
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask); ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask);
/* We assume that anything that has an alphabetic key in the // Ensure that we do not detect our own tsak faked keyboards
QWERTYUIOP range in it is the main keyboard. */ ioctl (fd, EVIOCGNAME (sizeof (name)), name);
for (j = KEY_Q; j <= KEY_P; j++) { if (str_ends_with(name, "+tsak") == 0) {
if (TestBit(j, key_bitmask)) /* We assume that anything that has an alphabetic key in the
return fd; 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; 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) bool setFileLock(int fd, bool close_on_failure)
{ {
struct flock fl; struct flock fl;
@ -154,8 +209,8 @@ bool setFileLock(int fd, bool close_on_failure)
fl.l_len = 1; fl.l_len = 1;
// Set the exclusive file lock // Set the exclusive file lock
if (fcntl(mPipe_fd_out, F_SETLK, &fl) == -1) { if (fcntl(fd, F_SETLK, &fl) == -1) {
close(mPipe_fd_out); close(fd);
return false; return false;
} }
@ -171,7 +226,7 @@ bool checkFileLock()
fl.l_whence = SEEK_SET; fl.l_whence = SEEK_SET;
fl.l_len = 0; 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. */ fcntl(fd, F_GETLK, &fl); /* Overwrites lock structure with preventors. */
if (fd > -1) { if (fd > -1) {
@ -202,6 +257,71 @@ bool setupPipe()
return setFileLock(mPipe_fd_out, true); 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 class PipeHandler
{ {
public: public:
@ -215,7 +335,7 @@ PipeHandler::PipeHandler()
PipeHandler::~PipeHandler() PipeHandler::~PipeHandler()
{ {
tearDownPipe(); tearDownLockingPipe();
} }
int main (int argc, char *argv[]) int main (int argc, char *argv[])
@ -223,13 +343,19 @@ int main (int argc, char *argv[])
struct input_event ev[64]; struct input_event ev[64];
struct input_event event; struct input_event event;
struct uinput_user_dev devinfo={0}; 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"; char name[256] = "Unknown";
bool ctrl_down = false; bool ctrl_down = false;
bool alt_down = false; bool alt_down = false;
bool hide_event = false; bool hide_event = false;
bool established = false; bool established = false;
bool testrun = 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 (argc == 2) {
if (strcmp(argv[1], "checkactive") == 0) { if (strcmp(argv[1], "checkactive") == 0) {
@ -239,7 +365,11 @@ int main (int argc, char *argv[])
// Check for existing file locks // Check for existing file locks
if (!checkFileLock()) { 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; return 8;
} }
@ -256,125 +386,227 @@ int main (int argc, char *argv[])
return 5; return 5;
} }
// Open Device // Find keyboards
fd = find_keyboard(); find_keyboards();
if (fd == -1) { if (keyboard_fd_num == 0) {
printf ("Could not find your keyboard!\n"); 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) if (established)
sleep(1); sleep(1);
else else {
return 4; int i=fork();
if (i<0) return 12; // fork failed
if (i>0) {
return 4;
}
sleep(1);
restart_tsak();
}
} }
else { else {
// Print Device Name fprintf(stderr, "Found %d keyboard(s)\n", keyboard_fd_num);
ioctl (fd, EVIOCGNAME (sizeof (name)), name);
fprintf(stderr, "Reading From : (%s)\n", name); can_proceed = true;
for (current_keyboard=0;current_keyboard<keyboard_fd_num;current_keyboard++) {
// Create filtered virtual output device // Print Device Name
devout=open("/dev/misc/uinput",O_WRONLY|O_NONBLOCK); ioctl (keyboard_fds[current_keyboard], EVIOCGNAME (sizeof (name)), name);
if (devout<0) { fprintf(stderr, "Reading from keyboard: (%s)\n", name);
perror("open(\"/dev/misc/uinput\")");
devout=open("/dev/uinput",O_WRONLY|O_NONBLOCK); // Create filtered virtual output device
} devout[current_keyboard]=open("/dev/misc/uinput",O_WRONLY|O_NONBLOCK);
if (devout<0) { if (devout[current_keyboard]<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"); devout[current_keyboard]=open("/dev/uinput",O_WRONLY|O_NONBLOCK);
perror("open(\"/dev/uinput\")"); if (devout[current_keyboard]<0) {
if (established) perror("open(\"/dev/misc/uinput\")");
sleep(1); }
else }
return 3; if (devout[current_keyboard]<0) {
} can_proceed = false;
else { 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");
if(ioctl(fd, EVIOCGRAB, 2) < 0) { perror("open(\"/dev/uinput\")");
close(fd);
fprintf(stderr, "Failed to grab exclusive input device lock");
if (established) if (established)
sleep(1); sleep(1);
else else
return 1; return 3;
} }
else { }
ioctl(fd, EVIOCGNAME(UINPUT_MAX_NAME_SIZE), devinfo.name);
strncat(devinfo.name, "+tsak", UINPUT_MAX_NAME_SIZE-1); if (can_proceed == true) {
fprintf(stderr, "%s\n", devinfo.name); for (current_keyboard=0;current_keyboard<keyboard_fd_num;current_keyboard++) {
ioctl(fd, EVIOCGID, &devinfo.id); if(ioctl(keyboard_fds[current_keyboard], EVIOCGRAB, 2) < 0) {
close(keyboard_fds[current_keyboard]);
copy_features(fd, devout); fprintf(stderr, "Failed to grab exclusive input device lock");
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 (established) if (established)
sleep(1); sleep(1);
else else
return 2; return 1;
} }
else { else {
fprintf(stderr,"Device created.\n"); ioctl(keyboard_fds[current_keyboard], EVIOCGNAME(UINPUT_MAX_NAME_SIZE), devinfo.name);
strncat(devinfo.name, "+tsak", UINPUT_MAX_NAME_SIZE-1);
if (established == false) { fprintf(stderr, "%s\n", devinfo.name);
tearDownPipe(); ioctl(keyboard_fds[current_keyboard], EVIOCGID, &devinfo.id);
int i=fork();
if (i<0) return 9; // fork failed copy_features(keyboard_fds[current_keyboard], devout[current_keyboard]);
if (i>0) { write(devout[current_keyboard],&devinfo,sizeof(devinfo));
// close parent process if (ioctl(devout[current_keyboard],UI_DEV_CREATE)<0) {
close(mPipe_fd_out); fprintf(stderr, "Unable to create input device with UI_DEV_CREATE\n");
return 0; if (established)
} sleep(1);
setupPipe(); else
return 2;
} }
else {
established = true; fprintf(stderr, "Device created.\n");
if (testrun == true) { if (established == false) {
return 0; int i=fork();
} if (i<0) return 9; // fork failed
if (i>0) {
while (1) { child_pids[current_keyboard] = i;
if ((rd = read (fd, ev, size * 2)) < size) { continue;
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;
} }
setupLockingPipe();
} }
if (value != ' ' && ev[1].value == 1 && ev[1].type == 1){ // Read the key press event
if (keycode[(ev[1].code)]) { established = true;
if (strcmp(keycode[(ev[1].code)], "<control>") == 0) ctrl_down = true;
if (strcmp(keycode[(ev[1].code)], "<alt>") == 0) alt_down = true; if (testrun == true) {
} return 0;
} }
hide_event = false; while (1) {
if (keycode[(ev[1].code)]) { if ((rd = read (keyboard_fds[current_keyboard], ev, size * 2)) < size) {
if (alt_down && ctrl_down && (strcmp(keycode[(ev[1].code)], "<del>") == 0)) { fprintf(stderr, "Read failed.\n");
hide_event = true; 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) { /* We assume that anything that has an alphabetic key in the
// Pass the event on... QWERTYUIOP range in it is the main keyboard. */
event = ev[0]; for (j = KEY_Q; j <= KEY_P; j++) {
write(devout, &event, sizeof event); if (TestBit(j, key_bitmask)) {
event = ev[1]; is_new_keyboard = true;
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);
}
} }
} }
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");
} }
} }
} }

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

Loading…
Cancel
Save