diff options
Diffstat (limited to 'tsak/main.cpp')
-rw-r--r-- | tsak/main.cpp | 838 |
1 files changed, 838 insertions, 0 deletions
diff --git a/tsak/main.cpp b/tsak/main.cpp new file mode 100644 index 000000000..2cac66a3a --- /dev/null +++ b/tsak/main.cpp @@ -0,0 +1,838 @@ +/* +Copyright 2010 Adam Marchetti +Copyright 2011-2013 Timothy Pearson <[email protected]> + +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 +published by the Free Software Foundation, either version 3 +of the License, or (at your option) any later version. + +tsak is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public +License along with tsak. If not, see http://www.gnu.org/licenses/. + +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <exception> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <dirent.h> +#include <linux/input.h> +#include <linux/uinput.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/select.h> +#include <sys/time.h> +#include <sys/wait.h> +#include <termios.h> +#include <signal.h> +extern "C" { +#include <libudev.h> +} +#include <libgen.h> + +using namespace std; + +#define FIFO_DIR "/tmp/tdesocket-global" +#define FIFO_FILE_OUT "/tmp/tdesocket-global/tsak" +#define FIFO_LOCKFILE_OUT "/tmp/tdesocket-global/tsak.lock" + +// WARNING +// MAX_KEYBOARDS must be greater than or equal to MAX_INPUT_NODE +#define MAX_KEYBOARDS 128 +#define MAX_INPUT_NODE 128 + +#define TestBit(bit, array) (array[(bit) / 8] & (1 << ((bit) % 8))) + +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]; +int child_led_pids[MAX_KEYBOARDS]; + +int current_keyboard = -1; +int devout[MAX_KEYBOARDS]; + +const char *keycode[256] = +{ + "", "<esc>", "1", "2", "3", "4", "5", "6", "7", "8", + "9", "0", "−", "=", "<backspace>", "<tab>", "q", "w", "e", "r", + "t", "y", "u", "i", "o", "p", "[", "]", "\n", "<control>", + "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", + "'", "", "<shift>", "\\", "z", "x", "c", "v", "b", "n", + "m", ",", ".", "/", "<shift>", "", "<alt>", " ", "<capslock>", + "<f1>", "<f2>", "<f3>", "<f4>", "<f5>", "<f6>", "<f7>", "<f8>", "<f9>", "<f10>", + "<numlock>", "<scrolllock>", "", "", "", "", "", "", "", "", + "", "", "\\", "f11", "f12", "", "", "", "", "", + "", "", "", "<control>", "", "<sysrq>", "", "", "<control>", "", "", + "<alt>", "", "", "", "", "", "", "", "", "", + "", "<del>", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "" +}; + +/* returns 1 if bit number i is set, otherwise returns 0 */ +int bit_set(size_t i, const byte* a) +{ + return a[i/CHAR_BIT] & (1 << i%CHAR_BIT); +} + +/* exception handling */ +struct exit_exception { + int c; + exit_exception(int c):c(c) { } +}; + +/* signal handler */ +void signal_callback_handler(int signum) +{ + // Terminate program + throw exit_exception(signum); +} + +/* termination handler */ +void tsak_friendly_termination() { + int i; + + if ((current_keyboard >= 0) && (devout[current_keyboard] > 0)) { + if (ioctl(devout[current_keyboard],UI_DEV_DESTROY)<0) { + fprintf(stderr, "[tsak] Unable to destroy input device with UI_DEV_DESTROY\n"); + } + else { + fprintf(stderr, "[tsak] Device destroyed\n"); + } + } + + // Close down all child processes + for (i=0; i<MAX_KEYBOARDS; i++) { + if (child_pids[i] != 0) { + kill(child_pids[i], SIGTERM); + } + if (child_led_pids[i] != 0) { + kill(child_led_pids[i], SIGTERM); + } + } + + // Wait for process termination + sleep(1); + + fprintf(stderr, "[tsak] tsak terminated by external request\n"); + exit(17); +} + +// -------------------------------------------------------------------------------------- +// 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 ); +} +// -------------------------------------------------------------------------------------- + +/* + * Set a file descriptor to blocking or non-blocking mode. + * + * @param fd The file descriptor + * @param blocking 0:non-blocking mode, 1:blocking mode + * + * @return 1:success, 0:failure. + */ +int fd_set_blocking(int fd, int blocking) +{ + /* Save the current flags */ + int flags = fcntl(fd, F_GETFL, 0); + if (flags == -1) { + return 0; + } + + if (blocking) { + flags &= ~O_NONBLOCK; + } + else { + flags |= O_NONBLOCK; + } + return fcntl(fd, F_SETFL, flags) != -1; +} + +/* 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) +{ + byte evtypes[EV_MAX/CHAR_BIT + 1] = {0}; + byte codes[KEY_MAX/CHAR_BIT + 1]; + unsigned i,code; + int op; + if (ioctl(devin, EVIOCGBIT(0, sizeof(evtypes)), evtypes) < 0) return; + for(i=0;i<EV_MAX;++i) { + if (bit_set(i, evtypes)) { + switch(i) { + case EV_KEY: op = UI_SET_KEYBIT; break; + case EV_REL: op = UI_SET_RELBIT; break; + case EV_ABS: op = UI_SET_ABSBIT; break; + case EV_MSC: op = UI_SET_MSCBIT; break; + case EV_LED: op = UI_SET_LEDBIT; break; + case EV_SND: op = UI_SET_SNDBIT; break; + case EV_SW: op = UI_SET_SWBIT; break; + default: op = -1; + } + } + if (op == -1) continue; + ioctl(devout, UI_SET_EVBIT, i); + memset(codes,0,sizeof(codes)); + if (ioctl(devin, EVIOCGBIT(i, sizeof(codes)), codes) >= 0) { + for(code=0;code<KEY_MAX;code++) { + if (bit_set(code, codes)) ioctl(devout, op, code); + } + } + } +} + +int find_keyboards() { + int i, j; + int fd; + char name[256] = "Unknown"; + + keyboard_fd_num = 0; + for (i=0; i<MAX_KEYBOARDS; i++) { + keyboard_fds[i] = 0; + } + + for (i=0; i<MAX_INPUT_NODE; i++) { + snprintf(filename, sizeof(filename), "/dev/input/event%d", i); + + fd = open(filename, O_RDWR|O_SYNC); + if (fd >= 0) { + ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask); + + // Ensure that we do not detect our own tsak faked keyboards + ioctl (fd, EVIOCGNAME(sizeof(name)), name); + if (str_ends_with(name, "+tsak") == 0) { + // Do not attempt to use virtual keyboards per Bug 1275 + struct input_id input_info; + ioctl (fd, EVIOCGID, &input_info); + if ((input_info.vendor != 0) && (input_info.product != 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++; + } + } + } + return 0; +} + +void tearDownPipe() +{ + if (mPipeOpen_out == true) { + mPipeOpen_out = false; + close(mPipe_fd_out); + unlink(FIFO_FILE_OUT); + } +} + +void tearDownLockingPipe() +{ + close(mPipe_lockfd_out); + unlink(FIFO_LOCKFILE_OUT); +} + +bool setFileLock(int fd, bool close_on_failure) +{ + struct flock fl; + + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 1; + + // Set the exclusive file lock + if (fcntl(fd, F_SETLK, &fl) == -1) { + close(fd); + return false; + } + + return true; +} + +bool checkFileLock() +{ + struct flock fl; + + fl.l_type = F_WRLCK; /* Test for any lock on any part of file. */ + fl.l_start = 0; + fl.l_whence = SEEK_SET; + fl.l_len = 0; + + int fd = open(FIFO_LOCKFILE_OUT, O_RDWR | O_NONBLOCK); + fcntl(fd, F_GETLK, &fl); /* Overwrites lock structure with preventors. */ + + if (fd > -1) { + if (fl.l_type == F_WRLCK) { + return false; + } + return true; + } + + return true; +} + +bool setupPipe() +{ + /* Create the FIFOs if they do not exist */ + umask(0); + mkdir(FIFO_DIR,0644); + + mknod(FIFO_FILE_OUT, S_IFIFO|0600, 0); + chmod(FIFO_FILE_OUT, 0600); + + mPipe_fd_out = open(FIFO_FILE_OUT, O_RDWR | O_NONBLOCK); + if (mPipe_fd_out > -1) { + mPipeOpen_out = true; + } + + // Set the exclusive file lock + return setFileLock(mPipe_fd_out, true); +} + +bool setupLockingPipe(bool writepid) +{ + /* Create the FIFOs as they may not exist */ + umask(0); + mkdir(FIFO_DIR,0644); + + mknod(FIFO_LOCKFILE_OUT, 0600, 0); + chmod(FIFO_LOCKFILE_OUT, 0600); + + mPipe_lockfd_out = open(FIFO_LOCKFILE_OUT, O_RDWR | O_NONBLOCK); + if (mPipe_lockfd_out > -1) { + if (writepid) { + // Write my PID to the file + pid_t tsakpid = getpid(); + char pidstring[1024]; + sprintf(pidstring, "%d", tsakpid); + write(mPipe_lockfd_out, pidstring, strlen(pidstring)); + } + // 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++) { + if (write(mPipe_fd_out, "SAK\n\r", 6) < 0) { + fprintf(stderr, "[tsak] Unable to send SAK signal to clients\n"); + } + } +} + +void restart_tsak() +{ + int i; + + fprintf(stderr, "[tsak] Forcibly terminating...\n"); + + // Close down all child processes + for (i=0; i<MAX_KEYBOARDS; i++) { + if (child_pids[i] != 0) { + kill(child_pids[i], SIGTERM); + } + if (child_led_pids[i] != 0) { + kill(child_led_pids[i], SIGTERM); + } + } + + // Wait for child process termination + for (i=0; i<MAX_KEYBOARDS; i++) { + if (child_pids[i] != 0) { + waitpid(child_pids[i], NULL, 0); + child_pids[i] = 0; + } + if (child_led_pids[i] != 0) { + waitpid(child_led_pids[i], NULL, 0); + child_led_pids[i] = 0; + } + } + + // Unset the exclusive file lock + if (mPipe_fd_out != -1) { + struct flock fl; + if (fcntl(mPipe_fd_out, F_UNLCK, &fl) == -1) { + fprintf(stderr, "[tsak] Failed to release exclusive pipe lock\n"); + } + close(mPipe_fd_out); + } + +#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: + PipeHandler(); + ~PipeHandler(); + + bool active; +}; + +PipeHandler::PipeHandler() +{ + active = false; +} + +PipeHandler::~PipeHandler() +{ + if (active) { + tearDownPipe(); + tearDownLockingPipe(); + } +} + +int main (int argc, char *argv[]) +{ + struct input_event ev[64]; + struct input_event event; + struct input_event revev; + struct uinput_user_dev devinfo={{0},{0}}; + int rd; + int i; + int 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; + bool depcheck = false; + bool can_proceed; + + // Ignore SIGPIPE + signal(SIGPIPE, SIG_IGN); + + // Register signal handlers + // Register signal and signal handler + signal(SIGINT, signal_callback_handler); + signal(SIGTERM, signal_callback_handler); + + set_terminate(tsak_friendly_termination); + + try { + for (i=0; i<MAX_KEYBOARDS; i++) { + child_pids[i] = 0; + child_led_pids[i] = 0; + } + + if (argc == 2) { + if (strcmp(argv[1], "checkactive") == 0) { + testrun = true; + } + if (strcmp(argv[1], "checkdeps") == 0) { + depcheck = true; + } + } + + if (depcheck == false) { + // Check for existing file locks + if (!checkFileLock()) { + fprintf(stderr, "[tsak] Another instance of this program is already running [1]\n"); + return 8; + } + if (!setupLockingPipe(true)) { + fprintf(stderr, "[tsak] Another instance of this program is already running [2]\n"); + return 8; + } + } + + // Create the output pipe + PipeHandler controlpipe; + if (depcheck == false) { + if (!setupPipe()) { + fprintf(stderr, "[tsak] Another instance of this program is already running\n"); + return 8; + } + } + + if ((testrun == false) && (depcheck == false)) { + // fork to background + int i=fork(); + if (i<0) { + return 10; // fork failed + } + if (i>0) { + // Terminate parent + controlpipe.active = false; + return 0; + } + } + + while (1) { + if (depcheck == false) { + controlpipe.active = true; + } + + if ((getuid ()) != 0) { + printf ("[tsak] You are not root! This WILL NOT WORK!\nDO NOT attempt to bypass security restrictions, e.g. by changing keyboard permissions or owner, if you want the SAK system to remain secure...\n"); + return 5; + } + + // Find keyboards + find_keyboards(); + if (keyboard_fd_num == 0) { + printf ("[tsak] Could not find any usable keyboard(s)!\n"); + if (depcheck == true) { + return 50; + } + // 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 { + int i=fork(); + if (i<0) { + return 12; // fork failed + } + if (i>0) { + return 4; + } + sleep(1); + restart_tsak(); + } + } + else { + fprintf(stderr, "[tsak] 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, "[tsak] Reading from keyboard: (%s)\n", name); + + // Create filtered virtual output device + devout[current_keyboard]=open("/dev/misc/uinput",O_RDWR|O_NONBLOCK); + if (devout[current_keyboard]<0) { + devout[current_keyboard]=open("/dev/uinput",O_RDWR|O_NONBLOCK); + if (devout[current_keyboard]<0) { + perror("open(\"/dev/misc/uinput\")"); + } + } + if (devout[current_keyboard]<0) { + can_proceed = false; + fprintf(stderr, "[tsak] 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; + } + fd_set_blocking(devout[current_keyboard], true); + } + if (depcheck == true) { + return 0; + } + + 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, "[tsak] Failed to grab exclusive input device lock\n"); + if (established) { + sleep(1); + } + else { + return 1; + } + } + else { + ioctl(keyboard_fds[current_keyboard], EVIOCGNAME(UINPUT_MAX_NAME_SIZE), devinfo.name); + strncat(devinfo.name, "+tsak", UINPUT_MAX_NAME_SIZE-1); + fprintf(stderr, "[tsak] %s\n", devinfo.name); + ioctl(keyboard_fds[current_keyboard], EVIOCGID, &devinfo.id); + + copy_features(keyboard_fds[current_keyboard], devout[current_keyboard]); + if (write(devout[current_keyboard],&devinfo,sizeof(devinfo)) < 0) { + fprintf(stderr, "[tsak] Unable to write to output device\n"); + } + if (ioctl(devout[current_keyboard],UI_DEV_CREATE)<0) { + fprintf(stderr, "[tsak] Unable to create input device with UI_DEV_CREATE\n"); + if (established) { + sleep(1); + } + else { + return 2; + } + } + else { + fprintf(stderr, "[tsak] Device created.\n"); + + if (established == false) { + int i=fork(); + if (i<0) return 9; // fork failed + if (i>0) { + child_pids[current_keyboard] = i; + + int i=fork(); + if (i<0) return 9; // fork failed + if (i>0) { + child_led_pids[current_keyboard] = i; + continue; + } + + if (testrun == true) { + return 0; + } + + while (1) { + // Replicate LED events from the virtual keyboard to the physical keyboard + int rrd = read(devout[current_keyboard], &revev, size); + if (rrd >= size) { + if ((revev.type == EV_LED) || (revev.type == EV_MSC)) { + if (write(keyboard_fds[current_keyboard], &revev, sizeof(revev)) < 0) { + fprintf(stderr, "[tsak] Unable to replicate LED event\n"); + } + } + } + } + return 0; + } + setupLockingPipe(false); + } + + established = true; + + if (testrun == true) { + return 0; + } + + while (1) { + if ((rd = read(keyboard_fds[current_keyboard], ev, size)) < size) { + fprintf(stderr, "[tsak] Read failed.\n"); + if (ioctl(devout[current_keyboard],UI_DEV_DESTROY)<0) { + fprintf(stderr, "[tsak] Unable to destroy input device with UI_DEV_DESTROY\n"); + return 13; + } + else { + fprintf(stderr, "[tsak] Device destroyed.\n"); + } + return 14; + } + + if (ev[0].value == 0 && ev[0].type == 1) { // Read the key release event + if (keycode[(ev[0].code)]) { + if (strcmp(keycode[(ev[0].code)], "<control>") == 0) ctrl_down = false; + if (strcmp(keycode[(ev[0].code)], "<alt>") == 0) alt_down = false; + } + } + if (ev[0].value == 1 && ev[0].type == 1) { // Read the key press event + if (keycode[(ev[0].code)]) { + if (strcmp(keycode[(ev[0].code)], "<control>") == 0) ctrl_down = true; + if (strcmp(keycode[(ev[0].code)], "<alt>") == 0) alt_down = true; + } + } + + hide_event = false; + if (ev[0].value == 1 && ev[0].type == 1) { // Read the key press event + if (keycode[(ev[0].code)]) { + if (alt_down && ctrl_down && (strcmp(keycode[(ev[0].code)], "<del>") == 0)) { + hide_event = true; + } + } + } + + if ((hide_event == false) && (ev[0].type != EV_LED) && (ev[0].type != EV_MSC)) { + // Pass the event on... + event = ev[0]; + if (write(devout[current_keyboard], &event, sizeof(event)) < 0) { + fprintf(stderr, "[tsak] Unable to replicate keyboard event!\n"); + } + } + if (hide_event == true) { + // Let anyone listening to our interface know that an SAK keypress was received + broadcast_sak(); + } + } + } + } + } + + // Close all keyboard file descriptors; we don't need them in this process and they can end up dangling/locked during forced restart + for (int current_keyboard=0;current_keyboard<keyboard_fd_num;current_keyboard++) { + close(keyboard_fds[current_keyboard]); + keyboard_fds[current_keyboard] = 0; + } + keyboard_fd_num = 0; + + if (testrun == true) { + return 0; + } + + // Prevent multiple process instances from starting + setupLockingPipe(true); + + // Wait a little bit so that udev hotplug can stabilize before we start monitoring + sleep(1); + + fprintf(stderr, "[tsak] 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, "[tsak] 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 + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(udev_monitor_get_fd(mon), &readfds); + int fdcount = select(udev_monitor_get_fd(mon)+1, &readfds, NULL, NULL, NULL); + if (fdcount < 0) { + if (errno == EINTR) { + fprintf(stderr, "[tsak] Signal caught in hotplug monitoring process; ignoring\n"); + } + else { + fprintf(stderr, "[tsak] Select failed on udev file descriptor in hotplug monitoring process\n"); + } + usleep(1000); + continue; + } + + 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); + + /* 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, "[tsak] Hotplugged new keyboard: (%s)\n", name); + udev_unref(udev); + restart_tsak(); + } + } + else { + fprintf(stderr, "[tsak] No device from receive_device(). A udev error has occurred; terminating hotplug monitoring process.\n"); + return 12; + } + } + + udev_unref(udev); + + fprintf(stderr, "[tsak] Hotplug monitoring process terminated\n"); + } + } + } + } + catch(exit_exception& e) { + tsak_friendly_termination(); + } + + return 6; +} |