Skip to content

Instantly share code, notes, and snippets.

@dgacitua
Last active May 18, 2021 16:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dgacitua/03dbec5fd5283615b44312e63bd3dabf to your computer and use it in GitHub Desktop.
Save dgacitua/03dbec5fd5283615b44312e63bd3dabf to your computer and use it in GitHub Desktop.
Local KeyUp/Keydown logger in Python3 for Linux (outputs to terminal)
# KeyUp/Keydown logger in Python3 (outputs to terminal)
# Based on https://blog.robertelder.org/detect-keyup-event-linux-terminal/
import signal
import re
import struct
import select
import os
import subprocess
class MyKeyEventClass3(object):
def on_fd_read(self, fd):
recv_return = bytearray(os.read(fd, struct.calcsize(self.event_bin_format)))
seconds, microseconds, e_type, code, value = struct.unpack(self.event_bin_format, recv_return)
full_time = (seconds * 1000) + round(microseconds / 1000)
s = self.get_keymap_as_string()
if e_type == 0x1: # 0x1 == EV_KEY means key press or release.
if s:
d = ("KeyUp" if value == 0 else "KeyDown") # value == 0 release, value == 1 press
keymap = self.parse_keymap_file(s)
if keymap is not None:
msg = 'ACTION: {action} - FD: {fd} - TIME: {time} - TYPE: {type} - CODE: {code} - KEY: {key}'.format(action = d, fd = str(fd), time = str(full_time), type = str(e_type), code = str(code), key = str(keymap[code]))
print(msg)
else:
print("Error while decoding keycode map.")
else:
print("Unable to obtain keycode map, perhaps you need to use 'sudo'?")
def __init__(self):
self.event_bin_format = 'llHHI' # See kernel documentation for 'struct input_event'
self.done = False
signal.signal(signal.SIGINT, self.cleanup)
self.poller = select.poll()
initial_event_mask = select.POLLIN | select.POLLPRI | select.POLLHUP | select.POLLERR
with open('/proc/bus/input/devices') as f:
devices_file_contents = f.read()
files = {}
for handlers in re.findall(r"""H: Handlers=([^\n]+)""", devices_file_contents, re.DOTALL):
dev_event_file = '/dev/input/event' + re.search(r'event(\d+)', handlers).group(1)
if 'kbd' in handlers:
try:
files[dev_event_file] = open(dev_event_file, 'rb')
# Listen for events on this socket:
self.poller.register(files[dev_event_file].fileno(), initial_event_mask)
print("Listening to " + str(dev_event_file) + " on fd " + str(files[dev_event_file].fileno()))
except IOError as e:
if e.strerror == 'Permission denied':
print("You don't have read permission on ({}). Are you root?".format(dev_event_file))
return
while not self.done:
try:
events = self.poller.poll(None)
for fd, flag in events:
if flag & (select.POLLIN | select.POLLPRI):
self.on_fd_read(fd)
if flag & (select.POLLHUP | select.POLLERR):
return # Lost the file descriptor
except Exception as e:
return # Probably interrupted system call
def cleanup(self, signum, frame):
self.done = True
def get_keymap_as_string(self):
try:
# External call to 'dumpkeys' executable.
child = subprocess.Popen('dumpkeys', stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = child.communicate()
except Exception as e:
print("An exception happend when trying to get the keymap data: " + str(e) + ". Note that you need to be root in order for dumpkeys to work.?")
return None
if child.returncode > 0:
print("Return code was " + str(child.returncode) + " when getting keymap data. stderr was " + str(stderr) + "")
return None
else:
return stdout.decode("utf-8")
def parse_keymap_file(self, s):
m = {}
try:
for line in s.splitlines():
trimmed_line = line.strip()
if re.match(r"^keycode.*", trimmed_line):
# Example format of the split format of each line we expect:
# ['keycode', '30', '=', '+a']
# Some keymappings will have multiple things they map to, but we just take the first one:
# ['keycode', '86', '=', 'less', 'greater', 'bar']
parts = line.split()
if(len(parts) >= 4):
m[int(parts[1])] = parts[3]
return m
except Exception as e:
print("An exception happend when trying to parse the keymap data: " + str(e) + ".")
return None
a = MyKeyEventClass3()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment