mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-22 23:48:12 +00:00
Pipe support & async pipe transport
This commit is contained in:
parent
cbc2be00ca
commit
456e1fba44
@ -13,14 +13,6 @@ class EventLoop:
|
|||||||
self.q = []
|
self.q = []
|
||||||
self.cnt = 0
|
self.cnt = 0
|
||||||
self.last_sleep = 0 # For performance stats
|
self.last_sleep = 0 # For performance stats
|
||||||
#self.button_cb = None
|
|
||||||
|
|
||||||
'''
|
|
||||||
def create_task(self, coro):
|
|
||||||
# CPython 3.4.2
|
|
||||||
self.call_at(0, coro)
|
|
||||||
# CPython asyncio incompatibility: we don't return Task object
|
|
||||||
'''
|
|
||||||
|
|
||||||
def call_soon(self, callback, *args):
|
def call_soon(self, callback, *args):
|
||||||
self.call_at(0, callback, *args)
|
self.call_at(0, callback, *args)
|
||||||
@ -78,27 +70,21 @@ class EventLoop:
|
|||||||
delay = arg
|
delay = arg
|
||||||
elif isinstance(ret, StopLoop):
|
elif isinstance(ret, StopLoop):
|
||||||
return arg
|
return arg
|
||||||
'''
|
# elif isinstance(ret, IORead):
|
||||||
elif isinstance(ret, IORead):
|
# self.add_reader(arg.fileno(), lambda self, c, f: self.call_soon(c, f), self, cb, arg)
|
||||||
self.add_reader(ret.obj.fileno(), lambda self, c, f: self.call_soon(c, f), self, cb, ret.obj)
|
# self.add_reader(arg.fileno(), lambda c, f: self.call_soon(c, f), cb, arg)
|
||||||
self.add_reader(ret.obj.fileno(), lambda c, f: self.call_soon(c, f), cb, ret.obj)
|
# self.add_reader(arg.fileno(), lambda cb: self.call_soon(cb), cb)
|
||||||
self.add_reader(arg.fileno(), lambda cb: self.call_soon(cb), cb)
|
# self.add_reader(arg.fileno(), cb)
|
||||||
self.add_reader(arg.fileno(), cb)
|
# continue
|
||||||
continue
|
# elif isinstance(ret, IOWrite):
|
||||||
elif isinstance(ret, IOWrite):
|
# self.add_writer(arg.fileno(), lambda cb: self.call_soon(cb), cb)
|
||||||
self.add_writer(arg.fileno(), lambda cb: self.call_soon(cb), cb)
|
# self.add_writer(arg.fileno(), cb)
|
||||||
self.add_writer(arg.fileno(), cb)
|
# continue
|
||||||
continue
|
# elif isinstance(ret, IOReadDone):
|
||||||
elif isinstance(ret, IOReadDone):
|
# self.remove_reader(arg.fileno())
|
||||||
self.remove_reader(arg.fileno())
|
# elif isinstance(ret, IOWriteDone):
|
||||||
elif isinstance(ret, IOWriteDone):
|
# self.remove_writer(arg.fileno())
|
||||||
self.remove_writer(arg.fileno())
|
|
||||||
'''
|
|
||||||
|
|
||||||
#elif isinstance(ret, IOButton):
|
|
||||||
# print("TADY")
|
|
||||||
# self.button_cb = cb
|
|
||||||
# continue
|
|
||||||
elif isinstance(ret, type_gen):
|
elif isinstance(ret, type_gen):
|
||||||
self.call_soon(ret)
|
self.call_soon(ret)
|
||||||
elif ret is None:
|
elif ret is None:
|
||||||
|
@ -1,73 +0,0 @@
|
|||||||
import uselect
|
|
||||||
import errno
|
|
||||||
|
|
||||||
from .core import EventLoop
|
|
||||||
|
|
||||||
if __debug__:
|
|
||||||
import logging
|
|
||||||
log = logging.getLogger("asyncio")
|
|
||||||
|
|
||||||
class EpollEventLoop(EventLoop):
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
EventLoop.__init__(self)
|
|
||||||
self.poller = uselect.poll()
|
|
||||||
self.objmap = {}
|
|
||||||
|
|
||||||
def add_reader(self, fd, cb, *args):
|
|
||||||
if __debug__:
|
|
||||||
log.debug("add_reader%s", (fd, cb, args))
|
|
||||||
if args:
|
|
||||||
self.poller.register(fd, uselect.POLLIN)
|
|
||||||
self.objmap[fd] = (cb, args)
|
|
||||||
else:
|
|
||||||
self.poller.register(fd, uselect.POLLIN)
|
|
||||||
self.objmap[fd] = cb
|
|
||||||
|
|
||||||
def remove_reader(self, fd):
|
|
||||||
if __debug__:
|
|
||||||
log.debug("remove_reader(%s)", fd)
|
|
||||||
self.poller.unregister(fd)
|
|
||||||
del self.objmap[fd]
|
|
||||||
|
|
||||||
def add_writer(self, fd, cb, *args):
|
|
||||||
if __debug__:
|
|
||||||
log.debug("add_writer%s", (fd, cb, args))
|
|
||||||
if args:
|
|
||||||
self.poller.register(fd, uselect.POLLOUT)
|
|
||||||
self.objmap[fd] = (cb, args)
|
|
||||||
else:
|
|
||||||
self.poller.register(fd, uselect.POLLOUT)
|
|
||||||
self.objmap[fd] = cb
|
|
||||||
|
|
||||||
def remove_writer(self, fd):
|
|
||||||
if __debug__:
|
|
||||||
log.debug("remove_writer(%s)", fd)
|
|
||||||
try:
|
|
||||||
self.poller.unregister(fd)
|
|
||||||
self.objmap.pop(fd, None)
|
|
||||||
except OSError as e:
|
|
||||||
# StreamWriter.awrite() first tries to write to an fd,
|
|
||||||
# and if that succeeds, yield IOWrite may never be called
|
|
||||||
# for that fd, and it will never be added to poller. So,
|
|
||||||
# ignore such error.
|
|
||||||
if e.args[0] != errno.ENOENT:
|
|
||||||
raise
|
|
||||||
|
|
||||||
def wait(self, delay):
|
|
||||||
if __debug__:
|
|
||||||
log.debug("epoll.wait(%d)", delay)
|
|
||||||
# We need one-shot behavior (second arg of 1 to .poll())
|
|
||||||
if delay == -1:
|
|
||||||
res = self.poller.poll(-1, 1)
|
|
||||||
else:
|
|
||||||
res = self.poller.poll(int(delay * 1000), 1)
|
|
||||||
# log.debug("epoll result: %s", res)
|
|
||||||
for fd, ev in res:
|
|
||||||
cb = self.objmap[fd]
|
|
||||||
if __debug__:
|
|
||||||
log.debug("Calling IO callback: %r", cb)
|
|
||||||
if isinstance(cb, tuple):
|
|
||||||
cb[0](*cb[1])
|
|
||||||
else:
|
|
||||||
self.call_soon(cb)
|
|
@ -1,94 +0,0 @@
|
|||||||
from collections.deque import deque
|
|
||||||
from uasyncio.core import sleep
|
|
||||||
|
|
||||||
|
|
||||||
class QueueEmpty(Exception):
|
|
||||||
"""Exception raised by get_nowait()."""
|
|
||||||
|
|
||||||
|
|
||||||
class QueueFull(Exception):
|
|
||||||
"""Exception raised by put_nowait()."""
|
|
||||||
|
|
||||||
|
|
||||||
class Queue:
|
|
||||||
"""A queue, useful for coordinating producer and consumer coroutines.
|
|
||||||
|
|
||||||
If maxsize is less than or equal to zero, the queue size is infinite. If it
|
|
||||||
is an integer greater than 0, then "yield from put()" will block when the
|
|
||||||
queue reaches maxsize, until an item is removed by get().
|
|
||||||
|
|
||||||
Unlike the standard library Queue, you can reliably know this Queue's size
|
|
||||||
with qsize(), since your single-threaded uasyncio application won't be
|
|
||||||
interrupted between calling qsize() and doing an operation on the Queue.
|
|
||||||
"""
|
|
||||||
_attempt_delay = 0.1
|
|
||||||
|
|
||||||
def __init__(self, maxsize=0):
|
|
||||||
self.maxsize = maxsize
|
|
||||||
self._queue = deque()
|
|
||||||
|
|
||||||
def _get(self):
|
|
||||||
return self._queue.popleft()
|
|
||||||
|
|
||||||
def get(self):
|
|
||||||
"""Returns generator, which can be used for getting (and removing)
|
|
||||||
an item from a queue.
|
|
||||||
|
|
||||||
Usage::
|
|
||||||
|
|
||||||
item = yield from queue.get()
|
|
||||||
"""
|
|
||||||
while not self._queue:
|
|
||||||
yield from sleep(self._attempt_delay)
|
|
||||||
return self._get()
|
|
||||||
|
|
||||||
def get_nowait(self):
|
|
||||||
"""Remove and return an item from the queue.
|
|
||||||
|
|
||||||
Return an item if one is immediately available, else raise QueueEmpty.
|
|
||||||
"""
|
|
||||||
if not self._queue:
|
|
||||||
raise QueueEmpty()
|
|
||||||
return self._get()
|
|
||||||
|
|
||||||
def _put(self, val):
|
|
||||||
self._queue.append(val)
|
|
||||||
|
|
||||||
def put(self, val):
|
|
||||||
"""Returns generator which can be used for putting item in a queue.
|
|
||||||
|
|
||||||
Usage::
|
|
||||||
|
|
||||||
yield from queue.put(item)
|
|
||||||
"""
|
|
||||||
while self.qsize() > self.maxsize and self.maxsize:
|
|
||||||
yield from sleep(self._attempt_delay)
|
|
||||||
self._put(val)
|
|
||||||
|
|
||||||
def put_nowait(self, val):
|
|
||||||
"""Put an item into the queue without blocking.
|
|
||||||
|
|
||||||
If no free slot is immediately available, raise QueueFull.
|
|
||||||
"""
|
|
||||||
if self.qsize() >= self.maxsize and self.maxsize:
|
|
||||||
raise QueueFull()
|
|
||||||
self._put(val)
|
|
||||||
|
|
||||||
def qsize(self):
|
|
||||||
"""Number of items in the queue."""
|
|
||||||
return len(self._queue)
|
|
||||||
|
|
||||||
def empty(self):
|
|
||||||
"""Return True if the queue is empty, False otherwise."""
|
|
||||||
return not self._queue
|
|
||||||
|
|
||||||
def full(self):
|
|
||||||
"""Return True if there are maxsize items in the queue.
|
|
||||||
|
|
||||||
Note: if the Queue was initialized with maxsize=0 (the default),
|
|
||||||
then full() is never True.
|
|
||||||
"""
|
|
||||||
if self.maxsize <= 0:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return self.qsize() >= self.maxsize
|
|
@ -1,139 +0,0 @@
|
|||||||
import usocket
|
|
||||||
import errno
|
|
||||||
from .core import IOReadDone, IOWriteDone, IORead, IOWrite
|
|
||||||
|
|
||||||
if __debug__:
|
|
||||||
import logging
|
|
||||||
log = logging.getLogger("asyncio")
|
|
||||||
|
|
||||||
class StreamReader:
|
|
||||||
|
|
||||||
def __init__(self, s):
|
|
||||||
self.s = s
|
|
||||||
|
|
||||||
def read(self, n=-1):
|
|
||||||
yield IORead(self.s)
|
|
||||||
while True:
|
|
||||||
res = self.s.read(n)
|
|
||||||
if res is not None:
|
|
||||||
break
|
|
||||||
log.warn("Empty read")
|
|
||||||
if not res:
|
|
||||||
yield IOReadDone(self.s)
|
|
||||||
return res
|
|
||||||
|
|
||||||
def readline(self):
|
|
||||||
if __debug__:
|
|
||||||
log.debug("StreamReader.readline()")
|
|
||||||
yield IORead(self.s)
|
|
||||||
# if __debug__:
|
|
||||||
# log.debug("StreamReader.readline(): after IORead: %s", s)
|
|
||||||
while True:
|
|
||||||
res = self.s.readline()
|
|
||||||
if res is not None:
|
|
||||||
break
|
|
||||||
log.warn("Empty read")
|
|
||||||
if not res:
|
|
||||||
yield IOReadDone(self.s)
|
|
||||||
if __debug__:
|
|
||||||
log.debug("StreamReader.readline(): res: %s", res)
|
|
||||||
return res
|
|
||||||
|
|
||||||
def aclose(self):
|
|
||||||
yield IOReadDone(self.s)
|
|
||||||
self.s.close()
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<StreamReader %r>" % self.s
|
|
||||||
|
|
||||||
|
|
||||||
class StreamWriter:
|
|
||||||
|
|
||||||
def __init__(self, s, extra):
|
|
||||||
self.s = s
|
|
||||||
self.extra = extra
|
|
||||||
|
|
||||||
def awrite(self, buf):
|
|
||||||
# This method is called awrite (async write) to not proliferate
|
|
||||||
# incompatibility with original asyncio. Unlike original asyncio
|
|
||||||
# whose .write() method is both not a coroutine and guaranteed
|
|
||||||
# to return immediately (which means it has to buffer all the
|
|
||||||
# data), this method is a coroutine.
|
|
||||||
sz = len(buf)
|
|
||||||
if __debug__:
|
|
||||||
log.debug("StreamWriter.awrite(): spooling %d bytes", sz)
|
|
||||||
while True:
|
|
||||||
res = self.s.write(buf)
|
|
||||||
# If we spooled everything, return immediately
|
|
||||||
if res == sz:
|
|
||||||
if __debug__:
|
|
||||||
log.debug("StreamWriter.awrite(): completed spooling %d bytes", res)
|
|
||||||
return
|
|
||||||
if res is None:
|
|
||||||
res = 0
|
|
||||||
if __debug__:
|
|
||||||
log.debug("StreamWriter.awrite(): spooled partial %d bytes", res)
|
|
||||||
assert res < sz
|
|
||||||
buf = buf[res:]
|
|
||||||
sz -= res
|
|
||||||
yield IOWrite(self.s)
|
|
||||||
# assert s2.fileno() == self.s.fileno()
|
|
||||||
if __debug__:
|
|
||||||
log.debug("StreamWriter.awrite(): can write more")
|
|
||||||
|
|
||||||
def aclose(self):
|
|
||||||
yield IOWriteDone(self.s)
|
|
||||||
self.s.close()
|
|
||||||
|
|
||||||
def get_extra_info(self, name, default=None):
|
|
||||||
return self.extra.get(name, default)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<StreamWriter %r>" % self.s
|
|
||||||
|
|
||||||
|
|
||||||
def open_connection(host, port):
|
|
||||||
if __debug__:
|
|
||||||
log.debug("open_connection(%s, %s)", host, port)
|
|
||||||
s = usocket.socket()
|
|
||||||
s.setblocking(False)
|
|
||||||
ai = usocket.getaddrinfo(host, port)
|
|
||||||
addr = ai[0][4]
|
|
||||||
try:
|
|
||||||
s.connect(addr)
|
|
||||||
except OSError as e:
|
|
||||||
if e.args[0] != errno.EINPROGRESS:
|
|
||||||
raise
|
|
||||||
if __debug__:
|
|
||||||
log.debug("open_connection: After connect")
|
|
||||||
yield IOWrite(s)
|
|
||||||
# if __debug__:
|
|
||||||
# assert s2.fileno() == s.fileno()
|
|
||||||
if __debug__:
|
|
||||||
log.debug("open_connection: After iowait: %s", s)
|
|
||||||
return StreamReader(s), StreamWriter(s, {})
|
|
||||||
|
|
||||||
|
|
||||||
def start_server(client_coro, host, port, backlog=10):
|
|
||||||
if __debug__:
|
|
||||||
log.debug("start_server(%s, %s)", host, port)
|
|
||||||
s = usocket.socket()
|
|
||||||
s.setblocking(False)
|
|
||||||
|
|
||||||
ai = usocket.getaddrinfo(host, port)
|
|
||||||
addr = ai[0][4]
|
|
||||||
s.setsockopt(usocket.SOL_SOCKET, usocket.SO_REUSEADDR, 1)
|
|
||||||
s.bind(addr)
|
|
||||||
s.listen(backlog)
|
|
||||||
while True:
|
|
||||||
if __debug__:
|
|
||||||
log.debug("start_server: Before accept")
|
|
||||||
yield IORead(s)
|
|
||||||
if __debug__:
|
|
||||||
log.debug("start_server: After iowait")
|
|
||||||
s2, client_addr = s.accept()
|
|
||||||
s2.setblocking(False)
|
|
||||||
if __debug__:
|
|
||||||
log.debug("start_server: After accept: %s", s2)
|
|
||||||
extra = {"peername": client_addr}
|
|
||||||
yield client_coro(StreamReader(s2), StreamWriter(s2, extra))
|
|
38
src/lib_linux/errno.py
Normal file
38
src/lib_linux/errno.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
EPERM = 1 # Operation not permitted
|
||||||
|
ENOENT = 2 # No such file or directory
|
||||||
|
ESRCH = 3 # No such process
|
||||||
|
EINTR = 4 # Interrupted system call
|
||||||
|
EIO = 5 # I/O error
|
||||||
|
ENXIO = 6 # No such device or address
|
||||||
|
E2BIG = 7 # Argument list too long
|
||||||
|
ENOEXEC = 8 # Exec format error
|
||||||
|
EBADF = 9 # Bad file number
|
||||||
|
ECHILD = 10 # No child processes
|
||||||
|
EAGAIN = 11 # Try again
|
||||||
|
ENOMEM = 12 # Out of memory
|
||||||
|
EACCES = 13 # Permission denied
|
||||||
|
EFAULT = 14 # Bad address
|
||||||
|
ENOTBLK = 15 # Block device required
|
||||||
|
EBUSY = 16 # Device or resource busy
|
||||||
|
EEXIST = 17 # File exists
|
||||||
|
EXDEV = 18 # Cross-device link
|
||||||
|
ENODEV = 19 # No such device
|
||||||
|
ENOTDIR = 20 # Not a directory
|
||||||
|
EISDIR = 21 # Is a directory
|
||||||
|
EINVAL = 22 # Invalid argument
|
||||||
|
ENFILE = 23 # File table overflow
|
||||||
|
EMFILE = 24 # Too many open files
|
||||||
|
ENOTTY = 25 # Not a typewriter
|
||||||
|
ETXTBSY = 26 # Text file busy
|
||||||
|
EFBIG = 27 # File too large
|
||||||
|
ENOSPC = 28 # No space left on device
|
||||||
|
ESPIPE = 29 # Illegal seek
|
||||||
|
EROFS = 30 # Read-only file system
|
||||||
|
EMLINK = 31 # Too many links
|
||||||
|
EPIPE = 32 # Broken pipe
|
||||||
|
EDOM = 33 # Math argument out of domain of func
|
||||||
|
ERANGE = 34 # Math result not representable
|
||||||
|
EAFNOSUPPORT = 97 # Address family not supported by protocol
|
||||||
|
ECONNRESET = 104 # Connection timed out
|
||||||
|
ETIMEDOUT = 110 # Connection timed out
|
||||||
|
EINPROGRESS = 115 # Operation now in progress
|
46
src/lib_linux/ffilib.py
Normal file
46
src/lib_linux/ffilib.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import sys
|
||||||
|
try:
|
||||||
|
import ffi
|
||||||
|
except ImportError:
|
||||||
|
ffi = None
|
||||||
|
|
||||||
|
_cache = {}
|
||||||
|
|
||||||
|
def open(name, maxver=10, extra=()):
|
||||||
|
if not ffi:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
return _cache[name]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
def libs():
|
||||||
|
if sys.platform == "linux":
|
||||||
|
yield '%s.so' % name
|
||||||
|
for i in range(maxver, -1, -1):
|
||||||
|
yield '%s.so.%u' % (name, i)
|
||||||
|
else:
|
||||||
|
for ext in ('dylib', 'dll'):
|
||||||
|
yield '%s.%s' % (name, ext)
|
||||||
|
for n in extra:
|
||||||
|
yield n
|
||||||
|
err = None
|
||||||
|
for n in libs():
|
||||||
|
try:
|
||||||
|
l = ffi.open(n)
|
||||||
|
_cache[name] = l
|
||||||
|
return l
|
||||||
|
except OSError as e:
|
||||||
|
err = e
|
||||||
|
raise err
|
||||||
|
|
||||||
|
def libc():
|
||||||
|
return open("libc", 6)
|
||||||
|
|
||||||
|
# Find out bitness of the platform, even if long ints are not supported
|
||||||
|
# TODO: All bitness differences should be removed from micropython-lib, and
|
||||||
|
# this snippet too.
|
||||||
|
bitness = 1
|
||||||
|
v = sys.maxsize
|
||||||
|
while v:
|
||||||
|
bitness += 1
|
||||||
|
v >>= 1
|
@ -41,6 +41,9 @@ if libc:
|
|||||||
opendir_ = libc.func("P", "opendir", "s")
|
opendir_ = libc.func("P", "opendir", "s")
|
||||||
readdir_ = libc.func("P", "readdir", "P")
|
readdir_ = libc.func("P", "readdir", "P")
|
||||||
open_ = libc.func("i", "open", "sii")
|
open_ = libc.func("i", "open", "sii")
|
||||||
|
fdopen_ = libc.func("i", "fdopen", "sii")
|
||||||
|
fsync_ = libc.func("i", "fsync", "i")
|
||||||
|
#lseek = libc.func("i", "lseek", "iii")
|
||||||
read_ = libc.func("i", "read", "ipi")
|
read_ = libc.func("i", "read", "ipi")
|
||||||
write_ = libc.func("i", "write", "iPi")
|
write_ = libc.func("i", "write", "iPi")
|
||||||
close_ = libc.func("i", "close", "i")
|
close_ = libc.func("i", "close", "i")
|
||||||
@ -48,6 +51,7 @@ if libc:
|
|||||||
access_ = libc.func("i", "access", "si")
|
access_ = libc.func("i", "access", "si")
|
||||||
fork_ = libc.func("i", "fork", "")
|
fork_ = libc.func("i", "fork", "")
|
||||||
pipe_ = libc.func("i", "pipe", "p")
|
pipe_ = libc.func("i", "pipe", "p")
|
||||||
|
mkfifo_ = libc.func("i", "mkfifo", "si")
|
||||||
_exit_ = libc.func("v", "_exit", "i")
|
_exit_ = libc.func("v", "_exit", "i")
|
||||||
getpid_ = libc.func("i", "getpid", "")
|
getpid_ = libc.func("i", "getpid", "")
|
||||||
waitpid_ = libc.func("i", "waitpid", "ipi")
|
waitpid_ = libc.func("i", "waitpid", "ipi")
|
||||||
@ -56,9 +60,6 @@ if libc:
|
|||||||
kill_ = libc.func("i", "kill", "ii")
|
kill_ = libc.func("i", "kill", "ii")
|
||||||
getenv_ = libc.func("s", "getenv", "P")
|
getenv_ = libc.func("s", "getenv", "P")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def check_error(ret):
|
def check_error(ret):
|
||||||
# Return True is error was EINTR (which usually means that OS call
|
# Return True is error was EINTR (which usually means that OS call
|
||||||
# should be restarted).
|
# should be restarted).
|
||||||
@ -163,6 +164,11 @@ def open(n, flags, mode=0o777):
|
|||||||
check_error(r)
|
check_error(r)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
def fdopen(n, flags, mode=0o777):
|
||||||
|
r = fdopen_(n, flags, mode)
|
||||||
|
check_error(r)
|
||||||
|
return r
|
||||||
|
|
||||||
def read(fd, n):
|
def read(fd, n):
|
||||||
buf = bytearray(n)
|
buf = bytearray(n)
|
||||||
r = read_(fd, buf, n)
|
r = read_(fd, buf, n)
|
||||||
@ -174,6 +180,11 @@ def write(fd, buf):
|
|||||||
check_error(r)
|
check_error(r)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
def fsync(fd):
|
||||||
|
r = fsync_(fd)
|
||||||
|
check_error(r)
|
||||||
|
return r
|
||||||
|
|
||||||
def close(fd):
|
def close(fd):
|
||||||
r = close_(fd)
|
r = close_(fd)
|
||||||
check_error(r)
|
check_error(r)
|
||||||
@ -202,6 +213,11 @@ def pipe():
|
|||||||
check_error(r)
|
check_error(r)
|
||||||
return a[0], a[1]
|
return a[0], a[1]
|
||||||
|
|
||||||
|
def mkfifo(n, mode=0o777):
|
||||||
|
r = mkfifo_(n, mode)
|
||||||
|
check_error(r)
|
||||||
|
return r
|
||||||
|
|
||||||
def _exit(n):
|
def _exit(n):
|
||||||
_exit_(n)
|
_exit_(n)
|
||||||
|
|
||||||
|
90
src/lib_linux/select.py
Normal file
90
src/lib_linux/select.py
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import ffi
|
||||||
|
import ustruct as struct
|
||||||
|
import os
|
||||||
|
import errno
|
||||||
|
import ffilib
|
||||||
|
|
||||||
|
|
||||||
|
libc = ffilib.libc()
|
||||||
|
|
||||||
|
#int epoll_create(int size);
|
||||||
|
epoll_create = libc.func("i", "epoll_create", "i")
|
||||||
|
#int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
|
||||||
|
epoll_ctl = libc.func("i", "epoll_ctl", "iiiP")
|
||||||
|
#int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
|
||||||
|
epoll_wait = libc.func("i", "epoll_wait", "ipii")
|
||||||
|
|
||||||
|
EPOLLIN = 0x001
|
||||||
|
EPOLLPRI = 0x002
|
||||||
|
EPOLLOUT = 0x004
|
||||||
|
EPOLLERR = 0x008
|
||||||
|
EPOLLHUP = 0x010
|
||||||
|
EPOLLRDHUP = 0x2000
|
||||||
|
EPOLLONESHOT = 1 << 30
|
||||||
|
EPOLLET = 1 << 31
|
||||||
|
|
||||||
|
EPOLL_CTL_ADD = 1
|
||||||
|
EPOLL_CTL_DEL = 2
|
||||||
|
EPOLL_CTL_MOD = 3
|
||||||
|
|
||||||
|
# TODO: struct epoll_event's 2nd member is union of uint64_t, etc.
|
||||||
|
# On x86, uint64_t is 4-byte aligned, on many other platforms - 8-byte.
|
||||||
|
# Until uctypes module can assign native struct offset, use dirty hack
|
||||||
|
# below.
|
||||||
|
# TODO: Get rid of all this dirtiness, move it on C side
|
||||||
|
if ffilib.bitness > 32:
|
||||||
|
# On x86_64, epoll_event is packed struct
|
||||||
|
epoll_event = "<IO"
|
||||||
|
elif struct.calcsize("IQ") == 12:
|
||||||
|
epoll_event = "IO"
|
||||||
|
else:
|
||||||
|
epoll_event = "QO"
|
||||||
|
|
||||||
|
class Epoll:
|
||||||
|
|
||||||
|
def __init__(self, epfd):
|
||||||
|
self.epfd = epfd
|
||||||
|
self.evbuf = struct.pack(epoll_event, 0, None)
|
||||||
|
self.registry = {}
|
||||||
|
|
||||||
|
def register(self, fd, eventmask=EPOLLIN|EPOLLPRI|EPOLLOUT, retval=None):
|
||||||
|
"retval is extension to stdlib, value to use in results from .poll()."
|
||||||
|
if retval is None:
|
||||||
|
retval = fd
|
||||||
|
s = struct.pack(epoll_event, eventmask, retval)
|
||||||
|
r = epoll_ctl(self.epfd, EPOLL_CTL_ADD, fd, s)
|
||||||
|
if r == -1 and os.errno_() == errno.EEXIST:
|
||||||
|
r = epoll_ctl(self.epfd, EPOLL_CTL_MOD, fd, s)
|
||||||
|
os.check_error(r)
|
||||||
|
# We must keep reference to retval, or it may be GCed. And we must
|
||||||
|
# keep mapping from fd to retval to be able to get rid of this retval
|
||||||
|
# reference later.
|
||||||
|
self.registry[fd] = retval
|
||||||
|
|
||||||
|
def unregister(self, fd):
|
||||||
|
# Pass dummy event structure, to workaround kernel bug
|
||||||
|
r = epoll_ctl(self.epfd, EPOLL_CTL_DEL, fd, self.evbuf)
|
||||||
|
os.check_error(r)
|
||||||
|
del self.registry[fd]
|
||||||
|
|
||||||
|
def poll(self, timeout=-1):
|
||||||
|
s = bytearray(self.evbuf)
|
||||||
|
while True:
|
||||||
|
n = epoll_wait(self.epfd, s, 1, timeout)
|
||||||
|
if not os.check_error(n):
|
||||||
|
break
|
||||||
|
# TODO: what about timeout value?
|
||||||
|
res = []
|
||||||
|
if n > 0:
|
||||||
|
vals = struct.unpack(epoll_event, s)
|
||||||
|
res.append((vals[1], vals[0]))
|
||||||
|
return res
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
os.close(self.epfd)
|
||||||
|
|
||||||
|
|
||||||
|
def epoll(sizehint=4):
|
||||||
|
fd = epoll_create(sizehint)
|
||||||
|
os.check_error(fd)
|
||||||
|
return Epoll(fd)
|
149
src/lib_linux/stat.py
Normal file
149
src/lib_linux/stat.py
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
"""Constants/functions for interpreting results of os.stat() and os.lstat().
|
||||||
|
|
||||||
|
Suggested usage: from stat import *
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Indices for stat struct members in the tuple returned by os.stat()
|
||||||
|
|
||||||
|
ST_MODE = 0
|
||||||
|
ST_INO = 1
|
||||||
|
ST_DEV = 2
|
||||||
|
ST_NLINK = 3
|
||||||
|
ST_UID = 4
|
||||||
|
ST_GID = 5
|
||||||
|
ST_SIZE = 6
|
||||||
|
ST_ATIME = 7
|
||||||
|
ST_MTIME = 8
|
||||||
|
ST_CTIME = 9
|
||||||
|
|
||||||
|
# Extract bits from the mode
|
||||||
|
|
||||||
|
def S_IMODE(mode):
|
||||||
|
"""Return the portion of the file's mode that can be set by
|
||||||
|
os.chmod().
|
||||||
|
"""
|
||||||
|
return mode & 0o7777
|
||||||
|
|
||||||
|
def S_IFMT(mode):
|
||||||
|
"""Return the portion of the file's mode that describes the
|
||||||
|
file type.
|
||||||
|
"""
|
||||||
|
return mode & 0o170000
|
||||||
|
|
||||||
|
# Constants used as S_IFMT() for various file types
|
||||||
|
# (not all are implemented on all systems)
|
||||||
|
|
||||||
|
S_IFDIR = 0o040000 # directory
|
||||||
|
S_IFCHR = 0o020000 # character device
|
||||||
|
S_IFBLK = 0o060000 # block device
|
||||||
|
S_IFREG = 0o100000 # regular file
|
||||||
|
S_IFIFO = 0o010000 # fifo (named pipe)
|
||||||
|
S_IFLNK = 0o120000 # symbolic link
|
||||||
|
S_IFSOCK = 0o140000 # socket file
|
||||||
|
|
||||||
|
# Functions to test for each file type
|
||||||
|
|
||||||
|
def S_ISDIR(mode):
|
||||||
|
"""Return True if mode is from a directory."""
|
||||||
|
return S_IFMT(mode) == S_IFDIR
|
||||||
|
|
||||||
|
def S_ISCHR(mode):
|
||||||
|
"""Return True if mode is from a character special device file."""
|
||||||
|
return S_IFMT(mode) == S_IFCHR
|
||||||
|
|
||||||
|
def S_ISBLK(mode):
|
||||||
|
"""Return True if mode is from a block special device file."""
|
||||||
|
return S_IFMT(mode) == S_IFBLK
|
||||||
|
|
||||||
|
def S_ISREG(mode):
|
||||||
|
"""Return True if mode is from a regular file."""
|
||||||
|
return S_IFMT(mode) == S_IFREG
|
||||||
|
|
||||||
|
def S_ISFIFO(mode):
|
||||||
|
"""Return True if mode is from a FIFO (named pipe)."""
|
||||||
|
return S_IFMT(mode) == S_IFIFO
|
||||||
|
|
||||||
|
def S_ISLNK(mode):
|
||||||
|
"""Return True if mode is from a symbolic link."""
|
||||||
|
return S_IFMT(mode) == S_IFLNK
|
||||||
|
|
||||||
|
def S_ISSOCK(mode):
|
||||||
|
"""Return True if mode is from a socket."""
|
||||||
|
return S_IFMT(mode) == S_IFSOCK
|
||||||
|
|
||||||
|
# Names for permission bits
|
||||||
|
|
||||||
|
S_ISUID = 0o4000 # set UID bit
|
||||||
|
S_ISGID = 0o2000 # set GID bit
|
||||||
|
S_ENFMT = S_ISGID # file locking enforcement
|
||||||
|
S_ISVTX = 0o1000 # sticky bit
|
||||||
|
S_IREAD = 0o0400 # Unix V7 synonym for S_IRUSR
|
||||||
|
S_IWRITE = 0o0200 # Unix V7 synonym for S_IWUSR
|
||||||
|
S_IEXEC = 0o0100 # Unix V7 synonym for S_IXUSR
|
||||||
|
S_IRWXU = 0o0700 # mask for owner permissions
|
||||||
|
S_IRUSR = 0o0400 # read by owner
|
||||||
|
S_IWUSR = 0o0200 # write by owner
|
||||||
|
S_IXUSR = 0o0100 # execute by owner
|
||||||
|
S_IRWXG = 0o0070 # mask for group permissions
|
||||||
|
S_IRGRP = 0o0040 # read by group
|
||||||
|
S_IWGRP = 0o0020 # write by group
|
||||||
|
S_IXGRP = 0o0010 # execute by group
|
||||||
|
S_IRWXO = 0o0007 # mask for others (not in group) permissions
|
||||||
|
S_IROTH = 0o0004 # read by others
|
||||||
|
S_IWOTH = 0o0002 # write by others
|
||||||
|
S_IXOTH = 0o0001 # execute by others
|
||||||
|
|
||||||
|
# Names for file flags
|
||||||
|
|
||||||
|
UF_NODUMP = 0x00000001 # do not dump file
|
||||||
|
UF_IMMUTABLE = 0x00000002 # file may not be changed
|
||||||
|
UF_APPEND = 0x00000004 # file may only be appended to
|
||||||
|
UF_OPAQUE = 0x00000008 # directory is opaque when viewed through a union stack
|
||||||
|
UF_NOUNLINK = 0x00000010 # file may not be renamed or deleted
|
||||||
|
UF_COMPRESSED = 0x00000020 # OS X: file is hfs-compressed
|
||||||
|
UF_HIDDEN = 0x00008000 # OS X: file should not be displayed
|
||||||
|
SF_ARCHIVED = 0x00010000 # file may be archived
|
||||||
|
SF_IMMUTABLE = 0x00020000 # file may not be changed
|
||||||
|
SF_APPEND = 0x00040000 # file may only be appended to
|
||||||
|
SF_NOUNLINK = 0x00100000 # file may not be renamed or deleted
|
||||||
|
SF_SNAPSHOT = 0x00200000 # file is a snapshot file
|
||||||
|
|
||||||
|
|
||||||
|
_filemode_table = (
|
||||||
|
((S_IFLNK, "l"),
|
||||||
|
(S_IFREG, "-"),
|
||||||
|
(S_IFBLK, "b"),
|
||||||
|
(S_IFDIR, "d"),
|
||||||
|
(S_IFCHR, "c"),
|
||||||
|
(S_IFIFO, "p")),
|
||||||
|
|
||||||
|
((S_IRUSR, "r"),),
|
||||||
|
((S_IWUSR, "w"),),
|
||||||
|
((S_IXUSR|S_ISUID, "s"),
|
||||||
|
(S_ISUID, "S"),
|
||||||
|
(S_IXUSR, "x")),
|
||||||
|
|
||||||
|
((S_IRGRP, "r"),),
|
||||||
|
((S_IWGRP, "w"),),
|
||||||
|
((S_IXGRP|S_ISGID, "s"),
|
||||||
|
(S_ISGID, "S"),
|
||||||
|
(S_IXGRP, "x")),
|
||||||
|
|
||||||
|
((S_IROTH, "r"),),
|
||||||
|
((S_IWOTH, "w"),),
|
||||||
|
((S_IXOTH|S_ISVTX, "t"),
|
||||||
|
(S_ISVTX, "T"),
|
||||||
|
(S_IXOTH, "x"))
|
||||||
|
)
|
||||||
|
|
||||||
|
def filemode(mode):
|
||||||
|
"""Convert a file's mode to a string of the form '-rwxrwxrwx'."""
|
||||||
|
perm = []
|
||||||
|
for table in _filemode_table:
|
||||||
|
for bit, char in table:
|
||||||
|
if mode & bit == bit:
|
||||||
|
perm.append(char)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
perm.append("-")
|
||||||
|
return "".join(perm)
|
141
src/lib_linux/transport_pipe.py
Normal file
141
src/lib_linux/transport_pipe.py
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
'''PipeTransport implements fake wire transport over local named pipe.
|
||||||
|
Use this transport for talking with trezor simulator.'''
|
||||||
|
|
||||||
|
import os
|
||||||
|
import ustruct
|
||||||
|
import uselect
|
||||||
|
|
||||||
|
from uasyncio import core
|
||||||
|
|
||||||
|
read_fd = None
|
||||||
|
write_fd = None
|
||||||
|
|
||||||
|
poll = None
|
||||||
|
on_read = None
|
||||||
|
|
||||||
|
def init(filename):
|
||||||
|
global read_fd, write_fd, poll
|
||||||
|
|
||||||
|
filename_read = filename + '.to'
|
||||||
|
filename_write = filename + '.from'
|
||||||
|
|
||||||
|
os.mkfifo(filename_read, 0o600)
|
||||||
|
os.mkfifo(filename_write, 0o600)
|
||||||
|
|
||||||
|
write_fd = os.open(filename_write, os.O_RDWR, 0o600)
|
||||||
|
read_fd = os.open(filename_read, os.O_RDWR, 0o600)
|
||||||
|
|
||||||
|
poll = uselect.poll()
|
||||||
|
poll.register(read_fd, uselect.POLLIN)
|
||||||
|
|
||||||
|
# Setup polling
|
||||||
|
loop = core.get_event_loop()
|
||||||
|
loop.call_soon(watch_read())
|
||||||
|
|
||||||
|
def set_notify(_on_read):
|
||||||
|
global on_read
|
||||||
|
on_read = _on_read
|
||||||
|
|
||||||
|
def close():
|
||||||
|
global read_fd, write_fd
|
||||||
|
|
||||||
|
os.close(read_fd)
|
||||||
|
os.close(write_fd)
|
||||||
|
|
||||||
|
def watch_read():
|
||||||
|
global on_read
|
||||||
|
sleep = core.Sleep(0.01)
|
||||||
|
while True:
|
||||||
|
if ready_to_read() and on_read:
|
||||||
|
on_read()
|
||||||
|
|
||||||
|
yield sleep
|
||||||
|
|
||||||
|
def ready_to_read():
|
||||||
|
global poll
|
||||||
|
return len(poll.poll(0)) > 0
|
||||||
|
|
||||||
|
def read():
|
||||||
|
"""
|
||||||
|
If there is data available to be read from the transport, reads the data and tries to parse it as a protobuf message. If the parsing succeeds, return a protobuf object.
|
||||||
|
Otherwise, returns None.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not ready_to_read():
|
||||||
|
return None
|
||||||
|
|
||||||
|
data = _read()
|
||||||
|
if data == None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return _parse_message(data)
|
||||||
|
|
||||||
|
def write(msg):
|
||||||
|
"""
|
||||||
|
Write mesage to tansport. msg should be a member of a valid `protobuf class <https://developers.google.com/protocol-buffers/docs/pythontutorial>`_ with a SerializeToString() method.
|
||||||
|
"""
|
||||||
|
ser = msg.SerializeToString()
|
||||||
|
header = ustruct.pack(">HL", mapping.get_type(msg), len(ser))
|
||||||
|
|
||||||
|
_write(b"##%s%s" % (header, ser))
|
||||||
|
|
||||||
|
def _parse_message(data):
|
||||||
|
(msg_type, _data) = data
|
||||||
|
if msg_type == 'protobuf':
|
||||||
|
return _data
|
||||||
|
else:
|
||||||
|
# inst = mapping.get_class(msg_type)()
|
||||||
|
# inst.ParseFromString(_data)
|
||||||
|
inst = _data
|
||||||
|
return inst
|
||||||
|
|
||||||
|
def _read_headers():
|
||||||
|
global read_fd
|
||||||
|
|
||||||
|
# Try to read headers until some sane value are detected
|
||||||
|
is_ok = False
|
||||||
|
while not is_ok:
|
||||||
|
|
||||||
|
# Align cursor to the beginning of the header ("##")
|
||||||
|
c = os.read(read_fd, 1)
|
||||||
|
i = 0
|
||||||
|
while c != b'#':
|
||||||
|
i += 1
|
||||||
|
if i >= 64:
|
||||||
|
# timeout
|
||||||
|
raise Exception("Timed out while waiting for the magic character")
|
||||||
|
# print "Aligning to magic characters"
|
||||||
|
c = os.read(read_fd, 1)
|
||||||
|
print(c)
|
||||||
|
|
||||||
|
if os.read(read_fd, 1) != b'#':
|
||||||
|
# Second character must be # to be valid header
|
||||||
|
raise Exception("Second magic character is broken")
|
||||||
|
|
||||||
|
# Now we're most likely on the beginning of the header
|
||||||
|
try:
|
||||||
|
headerlen = ustruct.calcsize(">HL")
|
||||||
|
(msg_type, datalen) = ustruct.unpack(">HL", os.read(read_fd, headerlen))
|
||||||
|
break
|
||||||
|
except:
|
||||||
|
raise Exception("Cannot parse header length")
|
||||||
|
|
||||||
|
return (msg_type, datalen)
|
||||||
|
|
||||||
|
def _write(msg):
|
||||||
|
global write_fd
|
||||||
|
try:
|
||||||
|
os.write(write_fd, msg)
|
||||||
|
# os.fsync(write_fd)
|
||||||
|
except OSError:
|
||||||
|
print("Error while writing to socket")
|
||||||
|
raise
|
||||||
|
|
||||||
|
def _read():
|
||||||
|
global read_fd
|
||||||
|
try:
|
||||||
|
(msg_type, datalen) = _read_headers()
|
||||||
|
return (msg_type, os.read(read_fd, datalen))
|
||||||
|
except:
|
||||||
|
print("Failed to read from device")
|
||||||
|
raise
|
@ -2,19 +2,26 @@
|
|||||||
import sys
|
import sys
|
||||||
sys.path.append('lib')
|
sys.path.append('lib')
|
||||||
|
|
||||||
|
if sys.platform == 'linux':
|
||||||
|
# Packages used only on linux platform (named pipes, ...)
|
||||||
|
sys.path.append('lib_linux')
|
||||||
|
|
||||||
import utime
|
import utime
|
||||||
import math
|
import math
|
||||||
import gc
|
import gc
|
||||||
|
|
||||||
from uasyncio import core
|
from uasyncio import core
|
||||||
from trezor import ui
|
# import transport_pipe as pipe
|
||||||
|
|
||||||
|
from trezor import ui, io
|
||||||
|
from trezor import msg2 as msg
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
loop = core.get_event_loop()
|
loop = core.get_event_loop()
|
||||||
|
|
||||||
def perf_info():
|
def perf_info():
|
||||||
mem_free = gc.mem_free()
|
mem_free = gc.mem_free()
|
||||||
# gc.collect()
|
gc.collect()
|
||||||
print("free_mem: %s/%s, last_sleep: %.06f" % \
|
print("free_mem: %s/%s, last_sleep: %.06f" % \
|
||||||
(mem_free, gc.mem_free(), loop.last_sleep))
|
(mem_free, gc.mem_free(), loop.last_sleep))
|
||||||
loop.call_later(1, perf_info)
|
loop.call_later(1, perf_info)
|
||||||
@ -87,9 +94,13 @@ def tap_to_confirm():
|
|||||||
|
|
||||||
yield core.Sleep(DELAY)
|
yield core.Sleep(DELAY)
|
||||||
|
|
||||||
|
def on_read():
|
||||||
|
print("READY TO READ")
|
||||||
|
print(msg.read())
|
||||||
|
|
||||||
def run():
|
def run():
|
||||||
# sekunda(3)
|
# pipe.init('../pipe', on_read)
|
||||||
# loop.call_soon(wait_for())
|
msg.set_notify(on_read)
|
||||||
|
|
||||||
loop.call_soon(perf_info)
|
loop.call_soon(perf_info)
|
||||||
loop.call_soon(tap_to_confirm())
|
loop.call_soon(tap_to_confirm())
|
||||||
|
18
src/trezor/msg2.py
Normal file
18
src/trezor/msg2.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
if sys.platform == 'linux':
|
||||||
|
import transport_pipe as pipe
|
||||||
|
|
||||||
|
def write(msg):
|
||||||
|
return pipe.write(msg)
|
||||||
|
|
||||||
|
def read():
|
||||||
|
return pipe.read()
|
||||||
|
|
||||||
|
def set_notify(_on_read):
|
||||||
|
return pipe.set_notify(_on_read)
|
||||||
|
|
||||||
|
pipe.init('../pipe')
|
||||||
|
|
||||||
|
else:
|
||||||
|
NotImplemented("HID transport")
|
Loading…
Reference in New Issue
Block a user