@ -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_keyboard s ( ) {
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_ LOCK FILE_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 ( )
{
{
tearDown Pipe( ) ;
tearDown Locking Pipe( ) ;
}
}
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). \n Possible 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). \n Possible 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 " ) ;
}
}
}
}
}
}