# Linux Kernel Keystroke Counter

## zojas

I hacked the keyboard driver in the kernel so it counts every keystroke and reports it through /proc/keystrokes.

You can go to http://www.desertsol.com/~kevin/keystroke_patch

to get a patch for 2.4.18 or 2.4.19-gentoo-r7.

Just patch, recompile, install, reboot, then

```
cat /proc/keystrokes
```

----------

## delta407

Moving to Tips & Tricks.

----------

## pjp

I'm curious... is this something akin to 'uptime'?

----------

## zojas

I just thought it would be interesting to see how many keys I hit during the normal course of a day. I found a project to count keystrokes for windows, but not for linux. so I spent about 2 hours hacking the kernel.

on a heavy day, I got over 100,000 keystrokes. typically I get less than that though.

root-tail comes in handy for showing a continuosly updated count in X. (I had to hack root-tail a bit though, because it only update once every 30 seconds; i got it to update every 3 seconds or so).

----------

## stig

Is something like this available for the 2.6 kernel series?

----------

## zojas

I never looked in to updating it. I have the patches for 2.4 posted at

http://desertsol.com/~kevin/keystroke_patch/

it might be easy to update, give it a whirl. let me know if you get it working.

----------

## centic

Sounds interesting, anyone got this working on 2.6-kernels? Also how hard will it be to do this as module to load on-demand?

Thx... Dominik.

----------

## dsf

 *centic wrote:*   

> Sounds interesting, anyone got this working on 2.6-kernels? Also how hard will it be to do this as module to load on-demand?
> 
> Thx... Dominik.

 

I ported it to 2.6 + module (should work ok as built-in, too).  :Smile: 

I'm still sorting out bugs but it seems to basically work. Now I need to convert from scan code to key code (because you can have up to 6 scan codes transmitted for a single actual 'key press') so that it will increment by 1 every time you hit a key and not by 4 or 6.

I also need to figure out how to unregister the input handler at module unload time -- instant crash if I don't do this.  :Smile:  (Reason: kernel is still trying to run code that no longer exists after the module is unloaded... so one HAS to unregister cleanly at module unload time!)

Then I should probably be all good. I'll post the heavily commented ~300 line source when debugged.

If zojas is reading this and wondering how I made it work in 2.6 as a module without hacking any existing kernel files -- the answer is simple: define your own input handler. It will then get called every time there's an input event, and within that input handler... look at only key events and ignore everything else. Works great.

I also made the keystroke counter a 64-bit value... should last you millions of trillions of keypresses before it hits the limit.  :Smile:  It probably would save an extra clock cycle or two in the top half handler if I made this unsigned 32-bit (4 billion), but no big deal.

Debugging kernel crashes in VMware is heavenly -- doesn't take out my actual workstation, and only takes 10 seconds to fully boot back up into Gentoo virtual machine.  :Smile: 

----------

## zojas

cool! yes I was wondering how you did it as a module.  :Smile: 

----------

## dsf

Version 1.0 of the 2.6 kernel driver (and modularized) is done.

This module is included in a 11K patch file. This works for gentoo-sources 2.6.11-r9, but probably should work fine for any recent 2.6 kernel.

http://www.globalcrossing.net/~dsf/linux/keystrokes-1.0.patch

To install this patch:

```
cd /usr/src/linux

patch -p1 < keystrokes-1.0.patch

make menuconfig

Device Drivers->Character devices->Keystroke Counting

Exit menuconfig and save new kernel configuration

make && make modules_install

modprobe keystrokes

dmesg|tail -n1
```

(The dmesg will verify it loaded without errors)

To see how many keystrokes you have entered since module loaded:

```
cat /proc/keystrokes
```

If you ever want to manually unload it:

```
modprobe -r keystrokes
```

If you want it to load at boot time, add one line to /etc/modules.autoload.d/kernel-2.6 that says:

```
keystrokes
```

You could build it into the kernel instead of as a module but I recommend module because it'll be easier to unload quickly if something goes wrong. I also have not tested this driver compiled-in-kernel.

Note: you will need to reapply the patch for each new kernel version since this is an unofficial patch.

Bug reports, questions, and comments are welcome.

----------

## mirko_3

Cool it works! I noticed that if you hold down ctrl or shift, for example, it starts increasing really fast as if I were pressing it repeatedly; not that it really matters, it doesn't have to be that accurate...

----------

## zojas

the keyboard repeat is kicking in, just like if you hold down the space bar or a normal key. 'kbrate' can be used to change the rate, and how soon the repeat kicks in.

----------

## mirko_3

Yeah, I know that, but what I meant was, is there no way around it? As I said, not that it really matters...

----------

## mirko_3

Also, anyone have a sed one-liner handy to remove the leading zeros? I'll search the manpage as soon as I get home, if noone has replied by then...

EDIT: this is what I've come up with, in case any other sed-illiterate needs it:

```

cat /proc/keystrokes|sed 's/^0*0//'

```

I suppose there may be prettier ways...

----------

## dsf

 *mirko_3 wrote:*   

> Also, anyone have a sed one-liner handy to remove the leading zeros? I'll search the manpage as soon as I get home, if noone has replied by then...
> 
> EDIT: this is what I've come up with, in case any other sed-illiterate needs it:
> 
> ```
> ...

 

That certainly works. Or you can edit /usr/src/linux/drivers/char/keystrokes.c and change from:

```
written = sprintf( buffer, "%016lld\n", keystrokes );
```

To:

```
written = sprintf( buffer, "%lld\n", keystrokes );
```

Then recompile and reinstall by:

```
# cd /usr/src/linux

# make && make modules_install

# modprobe -r keystrokes

# modprobe keystrokes
```

----------

## dsf

I wanted to pass along criticism of the kernel module that I did came from a friend (and I do immensely appreciate him bringing it up; no offense was taken at all).

Why? He's got good points. This kernel module generally works fine, but wouldn't have had been his preferred approach. Reason? /dev/input/event* is readable from user space and is easy for simple scripted or compiled tools to read from them. This is preferrable to the kernel stuff because you have far fewer restrictions (interrupt handling vs non-interrupt, having to protect data structures with mutexes, etc) and a goof in dealing with reading it in user space won't lead to a kernel panic or kernel oops. Also don't have to repatch the kernel every time it gets updated to a newer version.

With that said, I thought it was a fun way of spending a few hours this morning, so I don't have any regrets.  :Smile:  Besides, my ulterior motive was to blow the dust and cobwebs off my memory of the 2.6 kernel changes.  :Wink: 

It also doesn't make a bad template for future kernel modules, too. (With one major exception: most kernel modules would have a top half and bottom half handler. This one was so simple and quick that I left out a BH handler.)

And this is NOT a criticism of the original poster at all. This is still a pretty cool idea! It now better explains why the letters on my keyboard is wearing off.  :Smile: 

----------

## dsf

 *mirko_3 wrote:*   

> Yeah, I know that, but what I meant was, is there no way around it? As I said, not that it really matters...

 

I think this updated file will do the trick. I haven't tested all corner cases but it seems to work for the common case.

I've also taken out the zero padding, too.

If you've already previously applied the original v1.0 patch, just fetch the updated source file and rebuild.

http://www.globalcrossing.net/~dsf/linux/keystrokes.c

...and throw that file in /usr/src/linux/drivers/char/keystrokes.c then rebuild.

I've also updated the patch for 'new installs' at:

http://www.globalcrossing.net/~dsf/linux/keystrokes-1.0.1.patch

----------

## robbyjo

Wow thanks. This is sort of a motivation for me to hack my own kernel module.  :Very Happy:  I'll take this as an example. Again, thanks a lot.

----------

## gunkyman

Wow this is awesome! A nice addition to my torsmo ... now I realize I use my computer WAY too much T_T

----------

## centic

I really like the statement about the size of the counter in the patch:

```

+/*

+ * We chose an unsigned 64-bit type so that we don't end up limiting

+ * really, really power users that might hit the keyboard heavily and

+ * use their super-ultra-stable Linux system for years on end. :-)

+ *

+ * Note: that's slightly tongue-in-cheek as 2 to the 64th power is

+ * approximately, oh, 18.4 million trillion. It's more likely the

+ * keyboard will break long before, and system retired, too. :-)

+ */

static u_int64_t keystrokes=0;

```

I did a quick calculation assuming a user types 100 keys per second, which is a good rate of a typeist, especially for a prolonged amount of time.

This would lead to an overflow after (2^64)/100/60/24/365 years, which is something around 3.5E+11 years! So no need to worry, just happiliy type away, you'll never gonna reach that limit anyway!! Even with a 32-bit unsigned int it would take you more than 81 years to reach an overflow, already quite hard to do!

Dominik.

----------

## mirko_3

Hmmm... I see you've never seen me typing...  :Cool: 

 *centic wrote:*   

> I really like the statement about the size of the counter in the patch:
> 
> ```
> 
> +/*
> ...

 

----------

## zojas

 *dsf wrote:*   

>  *mirko_3 wrote:*   Yeah, I know that, but what I meant was, is there no way around it? As I said, not that it really matters... 
> 
> I think this updated file will do the trick. I haven't tested all corner cases but it seems to work for the common case.
> 
> 

 

I think your keyboard repeat code is too aggressive. if you type 3 'a's in a row, for example, it will only increase the count by 1.

----------

## mirko_3

Well, the fix fixed it, so I'm allright now...

----------

## zojas

my point is that if you type a word like 'teeth' the counter will only increment by 4, rather than the expected 5.

----------

## mirko_3

You're right, I just tested it...

----------

## robbyjo

Perhaps we can set the duplication to 3 or 4. But I can hardly think of words that has 3 identical letters in a row, except perhaps Kashyyyk. Hmm...

----------

## alinv

 *robbyjo wrote:*   

> Perhaps we can set the duplication to 3 or 4. But I can hardly think of words that has 3 identical letters in a row, except perhaps Kashyyyk. Hmm...

 

What about numbers like 1000000 or hex AAAABBBB1111...?

----------

## mirko_3

I mean... do you really need it to be that precise?

----------

## Redeeman

isnt it possible to catch a keypress event so that you just increment each time a key is pressed, and ignore the repeat thing?

----------

## dsf

People are indeed correct about the bug with repeats -- good eye and catch.  :Smile: 

Right now, the code assumes that if it has seen the same character before, that it is a repeat. This was required because of the way the code is written to ignore the actual keypress repeat events.

It could probably be restructured differently in some way, along with an additional check or two, and perhaps adding an additional variable or two to track this stuff.

So I will leave this as an exercise in code modification for anyone that may be interested in fixing it. Shouldn't be too hard.

----------

## rihteri

Nice hacks, but if you really only want to count the keystrokes, try this little python script I wrote inspired by dsf:s comment about doing this in userspace. Seems the problem is really simple.

```

i=0

event=file('/dev/input/event0','r')

while (1):

        event.read(96)

        i=i+1

        print i

```

Of course this only starts counting when it is started. Output could be better as well. But I think it counts right. You might have to alter the read size if you have non-i386-32bit processor, I think. And change the device if needed.

EDIT:

Seems like the event interface also outputs the time an event occurs.

```

struct input_event {

        struct timeval time;

        __u16 type;

        __u16 code;

        __s32 value;

};

```

Who wants to write a script to draw a graph of keystrokes relative to time? =)

----------

## zojas

where are these /dev/input/event* files documented? how did you know event0 was the keyboard?

----------

## rihteri

 *zojas wrote:*   

> where are these /dev/input/event* files documented? how did you know event0 was the keyboard?

 

http://www.frogmouth.net/hid-doco/p13.html has something. http://www.charmed.com/txt/input-programming.txt has some more.

About making the script... I tried 'cat /dev/input/event0' etc, noticed event0 is my keyboard, event1 is mouse etc. I was actually trying to get my synaptics touchpad working, thats why I came across this thred as well.

When you open up /usr/src/linux/include/input.h you can start guessing the format of an input event - seems I got the size right eventually, at least on my system. I'm really pretty clueless about this thing, I'm not even sure about anything I said before  :Razz: 

EDIT:

My script counts repeats wrong. It really needs some tuning.

----------

## mephist0

 *zojas wrote:*   

> I hacked the keyboard driver in the kernel so it counts every keystroke and reports it through /proc/keystrokes.
> 
> You can go to http://www.desertsol.com/~kevin/keystroke_patch
> 
> to get a patch for 2.4.18 or 2.4.19-gentoo-r7.
> ...

 

really cool, thanks alot !!

----------

## rihteri

I got interested in this thing, so heres a little improved version of the userspace keypress counter. Compile with 'g++ -o key key.cc'. Mostly ripped of from http://www.frogmouth.net/hid-doco/c193.html. Should work fine, ignores repeats and makes no (?) errors =)

key.cc:

```
#include <fstream>

#include <linux/input.h>

#include <iostream>

using namespace std;

int main() {

  struct input_event ev;

  int i = 0;

  

  //Open /dev/input/event0

  ifstream fd ("/dev/input/event0", ios::binary);

  if(! fd.good()) {

    cout << "failed to open /dev/input/event0, you are propably not root" << endl;

    exit(1);

  }

  while (1) {

    fd.read( reinterpret_cast<char *> (&ev), sizeof(struct input_event) );

    if (ev.value == 1) {

      i++;

      cout << i << " keys pressed." << endl;

    }

  }

}

```

----------

