#!/usr/bin/env python3 import signal import argparse import re import os import sys import time import dbus import dbus.mainloop.glib import dbus.service from binascii import hexlify, unhexlify from threading import Thread dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) from gi.repository import GLib import logging import logging.handlers from validitysensor import init from validitysensor.tls import tls from validitysensor.usb import usb from validitysensor.sid import sid_from_string from validitysensor.db import subtype_to_string, db from validitysensor.sensor import sensor, reboot, RebootException import pwd GLib.threads_init() INTERFACE_NAME='io.github.uunicorn.Fprint.Device' loop = GLib.MainLoop() usb.quit = lambda e: loop.quit() def uid2identity(uid): sidstr='S-1-5-21-111111111-1111111111-1111111111-%d' % uid return sid_from_string(sidstr) class Device(dbus.service.Object): def __init__(self, bus_name): dbus.service.Object.__init__(self, bus_name, '/io/github/uunicorn/Fprint/Device') @dbus.service.method(dbus_interface=INTERFACE_NAME, in_signature="s", out_signature="as") def ListEnrolledFingers(self, usr): try: logging.debug('In ListEnrolledFingers %s' % usr) pw=pwd.getpwnam(usr) uid=pw.pw_uid usr=db.lookup_user(uid2identity(uid)) if usr == None: return [] rc = [subtype_to_string(f['subtype']) for f in usr.fingers] logging.debug(repr(rc)) return rc except Exception as e: raise e @dbus.service.method(dbus_interface=INTERFACE_NAME, in_signature='s', out_signature='') def DeleteEnrolledFingers(self, user): logging.debug('In DeleteEnrolledFingers %s' % user) pw=pwd.getpwnam(user) usr=db.lookup_user(uid2identity(pw.pw_uid)) if usr == None: return db.del_record(usr.dbid) def do_scan(self): if self.capturing: return try: self.capturing = True z=identify() except Exception as e: #loop.quit(); raise e finally: self.capturing = False @dbus.service.method(dbus_interface=INTERFACE_NAME, in_signature='ss', out_signature='') def VerifyStart(self, user, finger): logging.debug('In VerifyStart %s' % finger) def update_cb(e): self.VerifyStatus('verify-retry-scan', False) def run(): try: usrid, subtype, hsh = sensor.identify(update_cb) self.VerifyStatus('verify-match', True) except Exception as e: logging.exception(e) self.VerifyStatus('verify-no-match', True) thread = Thread(target=run) thread.daemon = True thread.start() @dbus.service.method(dbus_interface=INTERFACE_NAME, in_signature='', out_signature='') def Cancel(self): sensor.cancel() @dbus.service.method(dbus_interface=INTERFACE_NAME, in_signature='ss', out_signature='') def EnrollStart(self, user, finger_name): logging.debug('In EnrollStart %s for %s' % (finger_name, user)) pw=pwd.getpwnam(user) uid=pw.pw_uid def update_cb(rsp, e): if e is not None: self.EnrollStatus('enroll-retry-scan', False) else: self.EnrollStatus('enroll-stage-passed', False) def run(): try: sensor.enroll(uid2identity(uid), 0xf5, update_cb) # TODO parse the finger name self.EnrollStatus('enroll-completed', True) except Exception as e: logging.exception(e) self.EnrollStatus('enroll-failed', True) thread = Thread(target=run) thread.daemon = True thread.start() @dbus.service.signal(dbus_interface=INTERFACE_NAME, signature='sb') def VerifyStatus(self, result, done): logging.debug('VerifyStatus') @dbus.service.signal(dbus_interface=INTERFACE_NAME, signature='sb') def EnrollStatus(self, result, done): logging.debug('EnrollStatus') @dbus.service.method(dbus_interface=INTERFACE_NAME, in_signature='s', out_signature='s') def RunCmd(self, cmd): logging.debug('RunCmd') return hexlify(tls.app(unhexlify(cmd))).decode() backoff_file='/usr/share/python-validity/backoff' # I don't know how to tell systemd to backoff in case of multiple instance of the same template service, help! def backoff(): if os.path.isfile(backoff_file): with open(backoff_file, 'r') as f: lines = list(map(float, f.readlines())) else: lines=[] while len(lines) > 0 and lines[0] + 60 < time.time(): lines=lines[1:] lines += [time.time()] with open(backoff_file, 'w') as f: for l in lines: f.write('%f\n' % l) if len(lines) > 10: raise Exception('Refusing to start more than 10 times per minute') if __name__ == '__main__': parser = argparse.ArgumentParser('Open fprintd DBus service') parser.add_argument('--debug', help='Enable tracing', action='store_true') parser.add_argument('--devpath', help='USB device path: usb--
') args = parser.parse_args() if args.debug: level = logging.DEBUG usb.trace_enabled = True tls.trace_enabled = True else: level = logging.INFO handler = logging.handlers.SysLogHandler(address = '/dev/log') logging.basicConfig(level=level, handlers=[handler]) backoff() try: if args.devpath is None: init.open() else: z = re.match('^usb-(\d+)-(\d+)$', args.devpath) if not z: parser.error('Option --devpath should look like this: usb--
') init.open_devpath(*map(int, z.groups())) except RebootException: logging.debug('Initialization ended up in rebooting the sensor. Normal exit.') sys.exit(0) bus = dbus.SystemBus() svc = Device(bus) watcher = None def watch_cb(name): if name == '': logging.debug('Manager is offline') else: logging.debug('Manager is back online, registering') mgr = bus.get_object(name, '/net/reactivated/Fprint/Manager') mgr = dbus.Interface(mgr, 'net.reactivated.Fprint.Manager') mgr.RegisterDevice(svc) watcher = bus.watch_name_owner('net.reactivated.Fprint', watch_cb) # Kick off the open-fprintd if it was not started yet bus.get_object('net.reactivated.Fprint', '/net/reactivated/Fprint/Manager') def die(x): logging.info('Cought signal %d. Stopping...' % x) loop.quit() GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, die, signal.SIGINT) GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGTERM, die, signal.SIGINT) try: loop.run() except Exception as e: logging.exception(e) raise e