[PATCH 6/8] host side for barebox remote control
Andrey Smirnov
andrew.smirnov at gmail.com
Sun Jan 17 17:07:46 PST 2016
One more time sans HTML
On Fri, Jan 8, 2016 at 3:13 AM, Sascha Hauer <s.hauer at pengutronix.de> wrote:
> From: Jan Luebbe <jlu at pengutronix.de>
>
> This contains the host tool for barebox remote control. It is written in
> Phython with its own implementation of the RATP protocol. Currently this
> is a very simple tool which needs more work, but the code can also be
> used as a library.
>
> Example output:
>
> console: '. '
> console: '.. '
> console: 'dev '
> console: 'env '
> console: 'mnt '
> console: '\n'
> Result: BBPacketCommandReturn(exit_code=0)
I have the following fix-ups for this patch:
-----------------------------8<-------------------
>From 6c89443301b0064aa424501da242dc7dfd29f3bb Mon Sep 17 00:00:00 2001
From: Andrey Smirnov <andrew.smirnov at gmail.com>
Date: Sun, 17 Jan 2016 15:58:03 -0800
Subject: [PATCH] fixup! host side for barebox remote control
remote needs to have __init__.py file in order to be valid python module
Signed-off-by: Andrey Smirnov <andrew.smirnov at gmail.com>
---
scripts/remote/__init__.py | 0
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 scripts/remote/__init__.py
diff --git a/scripts/remote/__init__.py b/scripts/remote/__init__.py
new file mode 100644
index 0000000..e69de29
--
2.5.0
----------------------------->8-------------------
-----------------------------8<-------------------
>From 61f5d56ea0ec9eb2bc87defb15751c51173ea003 Mon Sep 17 00:00:00 2001
From: Andrey Smirnov <andrew.smirnov at gmail.com>
Date: Sun, 17 Jan 2016 16:54:41 -0800
Subject: [PATCH] fixup! host side for barebox remote control
Add a call to os.path.expanduser so that paths starting with '~' would
be handled correctly
Signed-off-by: Andrey Smirnov <andrew.smirnov at gmail.com>
---
scripts/remote/ratpfs.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scripts/remote/ratpfs.py b/scripts/remote/ratpfs.py
index 0333bf7..91ca044 100644
--- a/scripts/remote/ratpfs.py
+++ b/scripts/remote/ratpfs.py
@@ -59,7 +59,7 @@ class RatpFSServer(object):
def __init__(self, path=None):
self.path = path
if path:
- self.path = os.path.abspath(path)
+ self.path = os.path.abspath(os.path.expanduser(path))
self.next_handle = 1 # 0 is invalid
self.files = {}
self.mounted = False
--
2.5.0
----------------------------->8-------------------
-----------------------------8<-------------------
>From b7a202a5cb53bdc52eb54fcdf808919519467001 Mon Sep 17 00:00:00 2001
From: Andrey Smirnov <andrew.smirnov at gmail.com>
Date: Sun, 17 Jan 2016 16:03:28 -0800
Subject: [PATCH] fixup! host side for barebox remote control
'event' variable is not really used anywhere
Signed-off-by: Andrey Smirnov <andrew.smirnov at gmail.com>
---
scripts/remote/main.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/scripts/remote/main.py b/scripts/remote/main.py
index 9350151..bd30472 100644
--- a/scripts/remote/main.py
+++ b/scripts/remote/main.py
@@ -95,8 +95,7 @@ def handle_console(args):
cons.start()
try:
while True:
- event = queue.get(block=True)
- src, data = event
+ src, data = queue.get(block=True)
if src == cons:
if data is None: # shutdown
cons.join()
--
2.5.0
----------------------------->8-------------------
>
> Signed-off-by: Jan Lübbe <j.luebbe at pengutronix.de>
> Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
With fixes above,
Tested-by: Andrey Smirnov <andrew.smirnov at gmail.com>
> ---
> scripts/bbremote | 3 +
> scripts/remote/controller.py | 173 ++++++++++
> scripts/remote/main.py | 169 +++++++++
> scripts/remote/messages.py | 154 +++++++++
> scripts/remote/missing.py | 28 ++
> scripts/remote/ratp.py | 773 ++++++++++++++++++++++++++++++++++++++++++
> scripts/remote/ratpfs.py | 189 +++++++++++
> scripts/remote/threadstdio.py | 47 +++
> 8 files changed, 1536 insertions(+)
> create mode 100755 scripts/bbremote
> create mode 100644 scripts/remote/controller.py
> create mode 100644 scripts/remote/main.py
> create mode 100644 scripts/remote/messages.py
> create mode 100644 scripts/remote/missing.py
> create mode 100644 scripts/remote/ratp.py
> create mode 100644 scripts/remote/ratpfs.py
> create mode 100644 scripts/remote/threadstdio.py
>
> diff --git a/scripts/bbremote b/scripts/bbremote
> new file mode 100755
> index 0000000..bc5351d
> --- /dev/null
> +++ b/scripts/bbremote
> @@ -0,0 +1,3 @@
> +#!/usr/bin/env python2
> +
> +import remote.main
> diff --git a/scripts/remote/controller.py b/scripts/remote/controller.py
> new file mode 100644
> index 0000000..a7257ec
> --- /dev/null
> +++ b/scripts/remote/controller.py
> @@ -0,0 +1,173 @@
> +#!/usr/bin/env python2
> +# -*- coding: utf-8 -*-
> +
> +from __future__ import absolute_import, division, print_function
> +
> +import struct
> +import logging
> +import sys
> +import os
> +from threading import Thread
> +from Queue import Queue, Empty
> +from .ratpfs import RatpFSServer
> +from .messages import *
> +from .ratp import RatpError
> +
> +try:
> + from time import monotonic
> +except:
> + from .missing import monotonic
> +
> +
> +def unpack(data):
> + p_type, = struct.unpack("!H", data[:2])
> + logging.debug("unpack: %r data=%r", p_type, repr(data))
> + if p_type == BBType.command:
> + logging.debug("received: command")
> + return BBPacketCommand(raw=data)
> + elif p_type == BBType.command_return:
> + logging.debug("received: command_return")
> + return BBPacketCommandReturn(raw=data)
> + elif p_type == BBType.consolemsg:
> + logging.debug("received: consolemsg")
> + return BBPacketConsoleMsg(raw=data)
> + elif p_type == BBType.ping:
> + logging.debug("received: ping")
> + return BBPacketPing(raw=data)
> + elif p_type == BBType.pong:
> + logging.debug("received: pong")
> + return BBPacketPong(raw=data)
> + elif p_type == BBType.getenv_return:
> + logging.debug("received: getenv_return")
> + return BBPacketGetenvReturn(raw=data)
> + elif p_type == BBType.fs:
> + logging.debug("received: fs")
> + return BBPacketFS(raw=data)
> + elif p_type == BBType.fs_return:
> + logging.debug("received: fs_return")
> + return BBPacketFSReturn(raw=data)
> + else:
> + logging.debug("received: UNKNOWN")
> + return BBPacket(raw=data)
> +
> +
> +class Controller(Thread):
> + def __init__(self, conn):
> + Thread.__init__(self)
> + self.daemon = True
> + self.conn = conn
> + self.fsserver = None
> + self.rxq = None
> + self.conn.connect(timeout=5.0)
> + self._txq = Queue()
> + self._stop = False
> + self.fsserver = RatpFSServer()
> +
> + def _send(self, bbpkt):
> + self.conn.send(bbpkt.pack())
> +
> + def _handle(self, bbpkt):
> + if isinstance(bbpkt, BBPacketConsoleMsg):
> + os.write(sys.stdout.fileno(), bbpkt.text)
> + elif isinstance(bbpkt, BBPacketPong):
> + print("pong",)
> + elif isinstance(bbpkt, BBPacketFS):
> + if self.fsserver != None:
> + self._send(self.fsserver.handle(bbpkt))
> +
> + def _expect(self, bbtype, timeout=1.0):
> + if timeout is not None:
> + limit = monotonic()+timeout
> + while timeout is None or limit > monotonic():
> + pkt = self.conn.recv(0.1)
> + if not pkt:
> + continue
> + bbpkt = unpack(pkt)
> + if isinstance(bbpkt, bbtype):
> + return bbpkt
> + else:
> + self._handle(bbpkt)
> +
> + def export(self, path):
> + self.fsserver = RatpFSServer(path)
> +
> + def ping(self):
> + self._send(BBPacketPing())
> + r = self._expect(BBPacketPong)
> + logging.info("Ping: %r", r)
> + if not r:
> + return 1
> + else:
> + print("pong")
> + return 0
> +
> + def command(self, cmd):
> + self._send(BBPacketCommand(cmd=cmd))
> + r = self._expect(BBPacketCommandReturn, timeout=None)
> + logging.info("Command: %r", r)
> + return r.exit_code
> +
> + def getenv(self, varname):
> + self._send(BBPacketGetenv(varname=varname))
> + r = self._expect(BBPacketGetenvReturn)
> + return r.text
> +
> + def close(self):
> + self.conn.close()
> +
> + def run(self):
> + assert self.rxq is not None
> + try:
> + while not self._stop:
> + # receive
> + pkt = self.conn.recv()
> + if pkt:
> + bbpkt = unpack(pkt)
> + if isinstance(bbpkt, BBPacketConsoleMsg):
> + self.rxq.put((self, bbpkt.text))
> + else:
> + self._handle(bbpkt)
> + # send
> + try:
> + pkt = self._txq.get(block=False)
> + except Empty:
> + pkt = None
> + if pkt:
> + self._send(pkt)
> + except RatpError as detail:
> + print("Ratp error:", detail, file=sys.stderr);
> + self.rxq.put((self, None))
> + return
> +
> + def start(self, queue):
> + assert self.rxq is None
> + self.rxq = queue
> + Thread.start(self)
> +
> + def stop(self):
> + self._stop = True
> + self.join()
> + self._stop = False
> + self.rxq = None
> +
> + def send_async(self, pkt):
> + self._txq.put(pkt)
> +
> + def send_async_console(self, text):
> + self._txq.put(BBPacketConsoleMsg(text=text))
> +
> + def send_async_ping(self):
> + self._txq.put(BBPacketPing())
> +
> +
> +def main():
> + import serial
> + from .ratp import SerialRatpConnection
> + url = "rfc2217://192.168.23.176:3002"
> + port = serial.serial_for_url(url, 115200)
> + conn = SerialRatpConnection(port)
> + ctrl = Controller(conn)
> + return ctrl
> +
> +if __name__ == "__main__":
> + C = main()
> diff --git a/scripts/remote/main.py b/scripts/remote/main.py
> new file mode 100644
> index 0000000..9350151
> --- /dev/null
> +++ b/scripts/remote/main.py
> @@ -0,0 +1,169 @@
> +#!/usr/bin/env python2
> +
> +from __future__ import absolute_import, division, print_function
> +
> +import sys
> +import os
> +import argparse
> +import logging
> +from Queue import Queue
> +from .ratp import RatpError
> +
> +try:
> + import serial
> +except:
> + print("error: No python-serial package found", file=sys.stderr)
> + exit(2)
> +
> +
> +def versiontuple(v):
> + return tuple(map(int, (v.split("."))))
> +
> +if versiontuple(serial.VERSION) < (2, 7):
> + print("warning: python-serial package is buggy in RFC2217 mode,",
> + "consider updating to at least 2.7", file=sys.stderr)
> +
> +from .ratp import SerialRatpConnection
> +from .controller import Controller
> +from .threadstdio import ConsoleInput
> +
> +
> +def get_controller(args):
> + port = serial.serial_for_url(args.port, args.baudrate)
> + conn = SerialRatpConnection(port)
> +
> + while True:
> + try:
> + ctrl = Controller(conn)
> + break
> + except (RatpError):
> + if args.wait == True:
> + pass
> + else:
> + raise
> +
> + return ctrl
> +
> +
> +def handle_run(args):
> + ctrl = get_controller(args)
> + ctrl.export(args.export)
> + res = ctrl.command(' '.join(args.arg))
> + if res:
> + res = 1
> + ctrl.close()
> + return res
> +
> +
> +def handle_ping(args):
> + ctrl = get_controller(args)
> + res = ctrl.ping()
> + if res:
> + res = 1
> + ctrl.close()
> + return res
> +
> +
> +def handle_getenv(args):
> + ctrl = get_controller(args)
> + value = ctrl.getenv(' '.join(args.arg))
> + if not value:
> + res = 1
> + else:
> + print(value)
> + res = 0
> + ctrl.close()
> + return res
> +
> +
> +def handle_listen(args):
> + port = serial.serial_for_url(args.port, args.baudrate)
> + conn = SerialRatpConnection(port)
> + conn.listen()
> + while True:
> + conn.wait(None)
> + conn.close()
> +
> +
> +def handle_console(args):
> + queue = Queue()
> + ctrl = get_controller(args)
> + ctrl.export(args.export)
> + ctrl.start(queue)
> + ctrl.send_async_console('\r')
> + cons = ConsoleInput(queue, exit='\x14') # CTRL-T
> + cons.start()
> + try:
> + while True:
> + event = queue.get(block=True)
> + src, data = event
> + if src == cons:
> + if data is None: # shutdown
> + cons.join()
> + break
> + elif data == '\x10': # CTRL-P
> + ctrl.send_async_ping()
> + else:
> + ctrl.send_async_console(data)
> + elif src == ctrl:
> + if data is None: # shutdown
> + sys.exit(1)
> + break
> + else:
> + os.write(sys.stdout.fileno(), data)
> + ctrl.stop()
> + ctrl.close()
> + finally:
> + print()
> + print("total retransmits=%i crc-errors=%i" % (
> + ctrl.conn.total_retransmits,
> + ctrl.conn.total_crc_errors))
> +
> +VERBOSITY = {
> + 0: logging.WARN,
> + 1: logging.INFO,
> + 2: logging.DEBUG,
> + }
> +
> +parser = argparse.ArgumentParser(prog='bbremote')
> +parser.add_argument('-v', '--verbose', action='count', default=0)
> +parser.add_argument('--port', type=str, default=os.environ.get('BBREMOTE_PORT', None))
> +parser.add_argument('--baudrate', type=int, default=os.environ.get('BBREMOTE_BAUDRATE', 115200))
> +parser.add_argument('--export', type=str, default=os.environ.get('BBREMOTE_EXPORT', None))
> +parser.add_argument('-w', '--wait', action='count', default=0)
> +subparsers = parser.add_subparsers(help='sub-command help')
> +
> +parser_run = subparsers.add_parser('run', help="run a barebox command")
> +parser_run.add_argument('arg', nargs='+', help="barebox command to run")
> +parser_run.set_defaults(func=handle_run)
> +
> +parser_ping = subparsers.add_parser('ping', help="test connection")
> +parser_ping.set_defaults(func=handle_ping)
> +
> +parser_ping = subparsers.add_parser('getenv', help="get a barebox environment variable")
> +parser_ping.add_argument('arg', nargs='+', help="variable name")
> +parser_ping.set_defaults(func=handle_getenv)
> +
> +parser_run = subparsers.add_parser('listen', help="listen for an incoming connection")
> +parser_run.set_defaults(func=handle_listen)
> +
> +parser_run = subparsers.add_parser('console', help="connect to the console")
> +parser_run.set_defaults(func=handle_console)
> +
> +args = parser.parse_args()
> +logging.basicConfig(level=VERBOSITY[args.verbose],
> + format='%(levelname)-8s %(module)-8s %(funcName)-16s %(message)s')
> +try:
> + res = args.func(args)
> + exit(res)
> +except RatpError as detail:
> + print("Ratp error:", detail, file=sys.stderr);
> + exit(127)
> +except KeyboardInterrupt:
> + print("\nInterrupted", file=sys.stderr);
> + exit(1)
> +#try:
> +# res = args.func(args)
> +#except Exception as e:
> +# print("error: failed to establish connection: %s" % e, file=sys.stderr)
> +# exit(2)
> diff --git a/scripts/remote/messages.py b/scripts/remote/messages.py
> new file mode 100644
> index 0000000..8e8495b
> --- /dev/null
> +++ b/scripts/remote/messages.py
> @@ -0,0 +1,154 @@
> +#!/usr/bin/env python2
> +# -*- coding: utf-8 -*-
> +
> +from __future__ import absolute_import, division, print_function
> +
> +import struct
> +
> +
> +class BBType(object):
> + command = 1
> + command_return = 2
> + consolemsg = 3
> + ping = 4
> + pong = 5
> + getenv = 6
> + getenv_return = 7
> + fs = 8
> + fs_return = 9
> +
> +
> +class BBPacket(object):
> + def __init__(self, p_type=0, p_flags=0, payload="", raw=None):
> + self.p_type = p_type
> + self.p_flags = p_flags
> + if raw is not None:
> + self.unpack(raw)
> + else:
> + self.payload = payload
> +
> + def __repr__(self):
> + return "BBPacket(%i, %i)" % (self.p_type, self.p_flags)
> +
> + def _unpack_payload(self, data):
> + self.payload = data
> +
> + def _pack_payload(self):
> + return self.payload
> +
> + def unpack(self, data):
> + self.p_type, self.p_flags = struct.unpack("!HH", data[:4])
> + self._unpack_payload(data[4:])
> +
> + def pack(self):
> + return struct.pack("!HH", self.p_type, self.p_flags) + \
> + self._pack_payload()
> +
> +
> +class BBPacketCommand(BBPacket):
> + def __init__(self, raw=None, cmd=None):
> + self.cmd = cmd
> + super(BBPacketCommand, self).__init__(BBType.command, raw=raw)
> +
> + def __repr__(self):
> + return "BBPacketCommand(cmd=%r)" % self.cmd
> +
> + def _unpack_payload(self, payload):
> + self.cmd = payload
> +
> + def _pack_payload(self):
> + return self.cmd
> +
> +
> +class BBPacketCommandReturn(BBPacket):
> + def __init__(self, raw=None, exit_code=None):
> + self.exit_code = exit_code
> + super(BBPacketCommandReturn, self).__init__(BBType.command_return,
> + raw=raw)
> +
> + def __repr__(self):
> + return "BBPacketCommandReturn(exit_code=%i)" % self.exit_code
> +
> + def _unpack_payload(self, data):
> + self.exit_code, = struct.unpack("!L", data[:4])
> +
> + def _pack_payload(self):
> + return struct.pack("!L", self.exit_code)
> +
> +
> +class BBPacketConsoleMsg(BBPacket):
> + def __init__(self, raw=None, text=None):
> + self.text = text
> + super(BBPacketConsoleMsg, self).__init__(BBType.consolemsg, raw=raw)
> +
> + def __repr__(self):
> + return "BBPacketConsoleMsg(text=%r)" % self.text
> +
> + def _unpack_payload(self, payload):
> + self.text = payload
> +
> + def _pack_payload(self):
> + return self.text
> +
> +
> +class BBPacketPing(BBPacket):
> + def __init__(self, raw=None):
> + super(BBPacketPing, self).__init__(BBType.ping, raw=raw)
> +
> + def __repr__(self):
> + return "BBPacketPing()"
> +
> +
> +class BBPacketPong(BBPacket):
> + def __init__(self, raw=None):
> + super(BBPacketPong, self).__init__(BBType.pong, raw=raw)
> +
> + def __repr__(self):
> + return "BBPacketPong()"
> +
> +
> +class BBPacketGetenv(BBPacket):
> + def __init__(self, raw=None, varname=None):
> + self.varname = varname
> + super(BBPacketGetenv, self).__init__(BBType.getenv, raw=raw)
> +
> + def __repr__(self):
> + return "BBPacketGetenv(varname=%r)" % self.varname
> +
> + def _unpack_payload(self, payload):
> + self.varname = payload
> +
> + def _pack_payload(self):
> + return self.varname
> +
> +
> +class BBPacketGetenvReturn(BBPacket):
> + def __init__(self, raw=None, text=None):
> + self.text = text
> + super(BBPacketGetenvReturn, self).__init__(BBType.getenv_return,
> + raw=raw)
> +
> + def __repr__(self):
> + return "BBPacketGetenvReturn(varvalue=%s)" % self.text
> +
> + def _unpack_payload(self, payload):
> + self.text = payload
> +
> + def _pack_payload(self):
> + return self.text
> +
> +
> +class BBPacketFS(BBPacket):
> + def __init__(self, raw=None, payload=None):
> + super(BBPacketFS, self).__init__(BBType.fs, payload=payload, raw=raw)
> +
> + def __repr__(self):
> + return "BBPacketFS(payload=%r)" % self.payload
> +
> +
> +class BBPacketFSReturn(BBPacket):
> + def __init__(self, raw=None, payload=None):
> + super(BBPacketFSReturn, self).__init__(BBType.fs_return, payload=payload, raw=raw)
> +
> + def __repr__(self):
> + return "BBPacketFSReturn(payload=%r)" % self.payload
> diff --git a/scripts/remote/missing.py b/scripts/remote/missing.py
> new file mode 100644
> index 0000000..67c2dfa
> --- /dev/null
> +++ b/scripts/remote/missing.py
> @@ -0,0 +1,28 @@
> +#!/usr/bin/env python
> +
> +import ctypes
> +import os
> +
> +CLOCK_MONOTONIC_RAW = 4 # from <linux/time.h>
> +
> +
> +class timespec(ctypes.Structure):
> + _fields_ = [
> + ('tv_sec', ctypes.c_long),
> + ('tv_nsec', ctypes.c_long)
> + ]
> +
> +librt = ctypes.CDLL('librt.so.1', use_errno=True)
> +clock_gettime = librt.clock_gettime
> +clock_gettime.argtypes = [ctypes.c_int, ctypes.POINTER(timespec)]
> +
> +
> +def monotonic():
> + t = timespec()
> + if clock_gettime(CLOCK_MONOTONIC_RAW, ctypes.pointer(t)) != 0:
> + errno_ = ctypes.get_errno()
> + raise OSError(errno_, os.strerror(errno_))
> + return t.tv_sec + t.tv_nsec * 1e-9
> +
> +if __name__ == "__main__":
> + print monotonic()
> diff --git a/scripts/remote/ratp.py b/scripts/remote/ratp.py
> new file mode 100644
> index 0000000..079fb87
> --- /dev/null
> +++ b/scripts/remote/ratp.py
> @@ -0,0 +1,773 @@
> +#!/usr/bin/env python2
> +# -*- coding: utf-8 -*-
> +
> +from __future__ import absolute_import, division, print_function
> +
> +import crcmod
> +import logging
> +import struct
> +from enum import Enum
> +from time import sleep
> +
> +try:
> + from time import monotonic
> +except:
> + from .missing import monotonic
> +
> +csum_func = crcmod.predefined.mkCrcFun('xmodem')
> +
> +
> +class RatpState(Enum):
> + listen = "listen" # 1
> + syn_sent = "syn-sent" # 2
> + syn_received = "syn-received" # 3
> + established = "established" # 4
> + fin_wait = "fin-wait" # 5
> + last_ack = "last-ack" # 6
> + closing = "closing" # 7
> + time_wait = "time-wait" # 8
> + closed = "closed" # 9
> +
> +
> +class RatpInvalidHeader(ValueError):
> + pass
> +
> +
> +class RatpInvalidPayload(ValueError):
> + pass
> +
> +
> +class RatpError(ValueError):
> + pass
> +
> +
> +class RatpPacket(object):
> +
> + def __init__(self, data=None, flags=''):
> + self.payload = None
> + self.synch = 0x01
> + self._control = 0
> + self.length = 0
> + self.csum = 0
> + self.c_syn = False
> + self.c_ack = False
> + self.c_fin = False
> + self.c_rst = False
> + self.c_sn = 0
> + self.c_an = 0
> + self.c_eor = False
> + self.c_so = False
> + if data:
> + (self.synch, self._control, self.length, self.csum) = \
> + struct.unpack('!BBBB', data)
> + if self.synch != 0x01:
> + raise RatpInvalidHeader("invalid synch octet (%x != %x)" %
> + (self.synch, 0x01))
> + csum = (self._control + self.length + self.csum) & 0xff
> + if csum != 0xff:
> + raise RatpInvalidHeader("invalid csum octet (%x != %x)" %
> + (csum, 0xff))
> + self._unpack_control()
> + elif flags:
> + if 'S' in flags:
> + self.c_syn = True
> + if 'A' in flags:
> + self.c_ack = True
> + if 'F' in flags:
> + self.c_fin = True
> + if 'R' in flags:
> + self.c_rst = True
> + if 'E' in flags:
> + self.c_eor = True
> +
> + def __repr__(self):
> + s = "RatpPacket("
> + if self.c_syn:
> + s += "SYN,"
> + if self.c_ack:
> + s += "ACK,"
> + if self.c_fin:
> + s += "FIN,"
> + if self.c_rst:
> + s += "RST,"
> + s += "SN=%i,AN=%i," % (self.c_sn, self.c_an)
> + if self.c_eor:
> + s += "EOR,"
> + if self.c_so:
> + s += "SO,DATA=%i)" % self.length
> + else:
> + s += "DATA=%i)" % self.length
> + return s
> +
> + def _pack_control(self):
> + self._control = 0 | \
> + self.c_syn << 7 | \
> + self.c_ack << 6 | \
> + self.c_fin << 5 | \
> + self.c_rst << 4 | \
> + self.c_sn << 3 | \
> + self.c_an << 2 | \
> + self.c_eor << 1 | \
> + self.c_so << 0
> +
> + def _unpack_control(self):
> + self.c_syn = bool(self._control & 1 << 7)
> + self.c_ack = bool(self._control & 1 << 6)
> + self.c_fin = bool(self._control & 1 << 5)
> + self.c_rst = bool(self._control & 1 << 4)
> + self.c_sn = bool(self._control & 1 << 3)
> + self.c_an = bool(self._control & 1 << 2)
> + self.c_eor = bool(self._control & 1 << 1)
> + self.c_so = bool(self._control & 1 << 0)
> +
> + def pack(self):
> + self._pack_control()
> + self.csum = 0
> + self.csum = (self._control + self.length + self.csum)
> + self.csum = (self.csum & 0xff) ^ 0xff
> + return struct.pack('!BBBB', self.synch, self._control, self.length,
> + self.csum)
> +
> + def unpack_payload(self, payload):
> + (c_recv,) = struct.unpack('!H', payload[-2:])
> + c_calc = csum_func(payload[:-2])
> + if c_recv != c_calc:
> + raise RatpInvalidPayload("bad checksum (%04x != %04x)" %
> + (c_recv, c_calc))
> + self.payload = payload[:-2]
> +
> + def pack_payload(self):
> + c_calc = csum_func(self.payload)
> + return self.payload+struct.pack('!H', c_calc)
> +
> +
> +class RatpConnection(object):
> + def __init__(self):
> + self._state = RatpState.closed
> + self._passive = True
> + self._input = b''
> + self._s_sn = 0
> + self._r_sn = 0
> + self._retrans = None
> + self._retrans_counter = None
> + self._retrans_deadline = None
> + self._r_mdl = None
> + self._s_mdl = 0xff
> + self._rx_buf = [] # reassembly buffer
> + self._rx_queue = []
> + self._tx_queue = []
> + self._rtt_alpha = 0.8
> + self._rtt_beta = 2.0
> + self._srtt = 0.2
> + self._rto_min, self._rto_max = 0.2, 1
> + self._tx_timestamp = None
> + self.total_retransmits = 0
> + self.total_crc_errors = 0
> +
> + def _update_srtt(self, rtt):
> + self._srtt = (self._rtt_alpha * self._srtt) + \
> + ((1.0 - self._rtt_alpha) * rtt)
> + logging.info("SRTT: %r", self._srtt)
> +
> + def _get_rto(self):
> + return min(self._rto_max,
> + max(self._rto_min, self._rtt_beta * self._srtt))
> +
> + def _write(self, pkt):
> +
> + if pkt.payload or pkt.c_so or pkt.c_syn or pkt.c_rst or pkt.c_fin:
> + self._s_sn = pkt.c_sn
> + if not self._retrans:
> + self._retrans = pkt
> + self._retrans_counter = 0
> + else:
> + self.total_retransmits += 1
> + self._retrans_counter += 1
> + if self._retrans_counter > 10:
> + raise RatpError("Maximum retransmit count exceeded")
> + self._retrans_deadline = monotonic()+self._get_rto()
> +
> + logging.info("Write: %r", pkt)
> +
> + self._write_raw(pkt.pack())
> + if pkt.payload:
> + self._write_raw(pkt.pack_payload())
> + self._tx_timestamp = monotonic()
> +
> + def _check_rto(self):
> + if self._retrans is None:
> + return
> +
> + if self._retrans_deadline < monotonic():
> + logging.debug("Retransmit...")
> + self._write(self._retrans)
> +
> + def _check_time_wait(self):
> + if not self._state == RatpState.time_wait:
> + return
> +
> + remaining = self._time_wait_deadline - monotonic()
> + if remaining < 0:
> + self._state = RatpState.closed
> + else:
> + logging.debug("Time-Wait: %.2f remaining" % remaining)
> + sleep(min(remaining, 0.1))
> +
> + def _read(self):
> + if len(self._input) < 4:
> + self._input += self._read_raw(4-len(self._input))
> + if len(self._input) < 4:
> + return
> +
> + try:
> + pkt = RatpPacket(data=self._input[:4])
> + except RatpInvalidHeader as e:
> + logging.info("%r", e)
> + self._input = self._input[1:]
> + return
> +
> + self._input = self._input[4:]
> +
> + logging.info("Read: %r", pkt)
> +
> + if pkt.c_syn or pkt.c_rst or pkt.c_so or pkt.c_fin:
> + return pkt
> +
> + if pkt.length == 0:
> + return pkt
> +
> + while len(self._input) < pkt.length+2:
> + self._input += self._read_raw()
> +
> + try:
> + pkt.unpack_payload(self._input[:pkt.length+2])
> + except RatpInvalidPayload as e:
> + self.total_crc_errors += 1
> + return
> + finally:
> + self._input = self._input[pkt.length+2:]
> +
> + return pkt
> +
> + def _close(self):
> + pass
> +
> + def _a(self, r):
> + logging.info("A")
> +
> + if r.c_rst:
> + return True
> +
> + if r.c_ack:
> + s = RatpPacket(flags='R')
> + s.c_sn = r.c_an
> + self._write(s)
> + return False
> +
> + if r.c_syn:
> + self._r_mdl = r.length
> +
> + s = RatpPacket(flags='SA')
> + s.c_sn = 0
> + s.c_an = (r.c_sn + 1) % 2
> + s.length = self._s_mdl
> + self._write(s)
> + self._state = RatpState.syn_received
> + return False
> +
> + return False
> +
> + def _b(self, r):
> + logging.info("B")
> +
> + if r.c_ack and r.c_an != (self._s_sn + 1) % 2:
> + if r.c_rst:
> + return False
> + else:
> + s = RatpPacket(flags='R')
> + s.c_sn = r.c_an
> + self._write(s)
> + return False
> +
> + if r.c_rst:
> + if r.c_ack:
> + self._retrans = None
> + # FIXME: delete the TCB
> + self._state = RatpState.closed
> + return False
> + else:
> + return False
> +
> + if r.c_syn:
> + if r.c_ack:
> + self._r_mdl = r.length
> + self._retrans = None
> + self._r_sn = r.c_sn
> + s = RatpPacket(flags='A')
> + s.c_sn = r.c_an
> + s.c_an = (r.c_sn + 1) % 2
> + self._write(s)
> + self._state = RatpState.established
> + return False
> + else:
> + self._retrans = None
> + s = RatpPacket(flags='SA')
> + s.c_sn = 0
> + s.c_an = (r.c_sn + 1) % 2
> + s.length = self._s_mdl
> + self._write(s)
> + self._state = RatpState.syn_received
> + return False
> +
> + return False
> +
> + def _c1(self, r):
> + logging.info("C1")
> +
> + if r.c_sn != self._r_sn:
> + return True
> +
> + if r.c_rst or r.c_fin:
> + return False
> +
> + s = RatpPacket(flags='A')
> + s.c_sn = r.c_an
> + s.c_an = (r.c_sn + 1) % 2
> + self._write(s)
> + return False
> +
> + def _c2(self, r):
> + logging.info("C2")
> +
> + if r.length == 0 and r.c_so == 0:
> + return True
> +
> + if r.c_sn != self._r_sn:
> + return True
> +
> + if r.c_rst or r.c_fin:
> + return False
> +
> + if r.c_syn:
> + s = RatpPacket(flags='RA')
> + s.c_sn = r.c_an
> + s.c_an = (r.c_sn + 1) % 2
> + self._write(s)
> + self._retrans = None
> + # FIXME: inform the user "Error: Connection reset"
> + self._state = RatpState.closed
> + return False
> +
> + # FIXME: only ack duplicate data packages?
> + # This is not documented in RFC 916
> + if r.length or r.c_so:
> + logging.info("C2: duplicate data packet, dropping")
> + s = RatpPacket(flags='A')
> + s.c_sn = r.c_an
> + s.c_an = (r.c_sn + 1) % 2
> + self._write(s)
> +
> + return False
> +
> + def _d1(self, r):
> + logging.info("D1")
> +
> + if not r.c_rst:
> + return True
> +
> + if self._passive:
> + self._retrans = None
> + self._state = RatpState.listen
> + return False
> + else:
> + self._retrans = None
> +
> + self._state = RatpState.closed
> + raise RatpError("Connection refused")
> +
> + def _d2(self, r):
> + logging.info("D2")
> +
> + if not r.c_rst:
> + return True
> +
> + self._retrans = None
> +
> + self._state = RatpState.closed
> +
> + raise RatpError("Connection reset")
> +
> + def _d3(self, r):
> + logging.info("C3")
> +
> + if not r.c_rst:
> + return True
> +
> + self._state = RatpState.closed
> + return False
> +
> + def _e(self, r):
> + logging.info("E")
> +
> + if not r.c_syn:
> + return True
> +
> + self._retrans = None
> + s = RatpPacket(flags='R')
> + if r.c_ack:
> + s.c_sn = r.c_an
> + else:
> + s.c_sn = 0
> + self._write(s)
> + self._state = RatpState.closed
> + raise RatpError("Connection reset")
> +
> + def _f1(self, r):
> + logging.info("F1")
> +
> + if not r.c_ack:
> + return False
> +
> + if r.c_an == (self._s_sn + 1) % 2:
> + return True
> +
> + if self._passive:
> + self._retrans = None
> + s = RatpPacket(flags='R')
> + s.c_sn = r.c_an
> + self._write(s)
> + self._state = RatpState.listen
> + return False
> + else:
> + self._retrans = None
> + s = RatpPacket(flags='R')
> + s.c_sn = r.c_an
> + self._write(s)
> + self._state = RatpState.closed
> + raise RatpError("Connection refused")
> +
> + def _f2(self, r):
> + logging.info("F2")
> +
> + if not r.c_ack:
> + return False
> +
> + if r.c_an == (self._s_sn + 1) % 2:
> + if self._retrans:
> + self._retrans = None
> + self._update_srtt(monotonic()-self._tx_timestamp)
> + # FIXME: inform the user with an "Ok" if a buffer has been
> + # entirely acknowledged. Another packet containing data may
> + # now be sent.
> + return True
> +
> + return True
> +
> + def _f3(self, r):
> + logging.info("F3")
> +
> + if not r.c_ack:
> + return False
> +
> + if r.c_an == (self._s_sn + 1) % 2:
> + return True
> +
> + return True
> +
> + def _g(self, r):
> + logging.info("G")
> +
> + if not r.c_rst:
> + return False
> +
> + self._retrans = None
> + if r.c_ack:
> + s = RatpPacket(flags='R')
> + s.c_sn = r.c_an
> + self._write(s)
> + else:
> + s = RatpPacket(flags='RA')
> + s.c_sn = r.c_an
> + s.c_an = (r.c_sn + 1) % 2
> + self._write(s)
> +
> + return False
> +
> + def _h1(self, r):
> + logging.info("H1")
> +
> + # FIXME: initial data?
> + self._state = RatpState.established
> + self._r_sn = r.c_sn
> +
> + return False
> +
> + def _h2(self, r):
> + logging.info("H2")
> +
> + if not r.c_fin:
> + return True
> +
> + if self._retrans is not None:
> + # FIXME: inform the user "Warning: Data left unsent.", "Connection closing."
> + self._retrans = None
> + s = RatpPacket(flags='FA')
> + s.c_sn = r.c_an
> + s.c_an = (r.c_sn + 1) % 2
> + self._write(s)
> + self._state = RatpState.last_ack
> + raise RatpError("Connection closed by remote")
> +
> + def _h3(self, r):
> + logging.info("H3")
> +
> + if not r.c_fin:
> + # Our fin was lost, rely on retransmission
> + return False
> +
> + if r.length or r.c_so:
> + self._retrans = None
> + s = RatpPacket(flags='RA')
> + s.c_sn = r.c_an
> + s.c_an = (r.c_sn + 1) % 2
> + self._write(s)
> + self._state = RatpState.closed
> + raise RatpError("Connection reset")
> +
> + if r.c_an == (self._s_sn + 1) % 2:
> + self._retrans = None
> + s = RatpPacket(flags='A')
> + s.c_sn = r.c_an
> + s.c_an = (r.c_sn + 1) % 2
> + self._write(s)
> + self._time_wait_deadline = monotonic() + self._get_rto()
> + self._state = RatpState.time_wait
> + return False
> + else:
> + self._retrans = None
> + s = RatpPacket(flags='A')
> + s.c_sn = r.c_an
> + s.c_an = (r.c_sn + 1) % 2
> + self._write(s)
> + self._state = RatpState.closing
> + return False
> +
> + def _h4(self, r):
> + logging.info("H4")
> +
> + if r.c_an == (self._s_sn + 1) % 2:
> + self._retrans = None
> + self._time_wait_deadline = monotonic() + self._get_rto()
> + self._state = RatpState.time_wait
> + return False
> +
> + return False
> +
> + def _h5(self, r):
> + logging.info("H5")
> +
> + if r.c_an == (self._s_sn + 1) % 2:
> + self._time_wait_deadline = monotonic() + self._get_rto()
> + self._state = RatpState.time_wait
> + return False
> +
> + return False
> +
> + def _h6(self, r):
> + logging.info("H6")
> +
> + if not r.c_ack:
> + return False
> +
> + if not r.c_fin:
> + return False
> +
> + self._retrans = None
> + s = RatpPacket(flags='A')
> + s.c_sn = r.c_an
> + s.c_an = (r.c_sn + 1) % 2
> + self._write(s)
> + self._time_wait_deadline = monotonic() + self._get_rto()
> + return False
> +
> + def _i1(self, r):
> + logging.info("I1")
> +
> + if r.c_so:
> + self._r_sn = r.c_sn
> + self._rx_buf.append(chr(r.length))
> + elif r.length:
> + self._r_sn = r.c_sn
> + self._rx_buf.append(r.payload)
> + else:
> + return False
> +
> + # reassemble
> + if r.c_eor:
> + logging.info("Reassembling %i frames", len(self._rx_buf))
> + self._rx_queue.append(''.join(self._rx_buf))
> + self._rx_buf = []
> +
> + s = RatpPacket(flags='A')
> + s.c_sn = r.c_an
> + s.c_an = (r.c_sn + 1) % 2
> + self._write(s)
> + return False
> +
> + def _machine(self, pkt):
> + logging.info("State: %r", self._state)
> + if self._state == RatpState.listen:
> + self._a(pkt)
> + elif self._state == RatpState.syn_sent:
> + self._b(pkt)
> + elif self._state == RatpState.syn_received:
> + self._c1(pkt) and \
> + self._d1(pkt) and \
> + self._e(pkt) and \
> + self._f1(pkt) and \
> + self._h1(pkt)
> + elif self._state == RatpState.established:
> + self._c2(pkt) and \
> + self._d2(pkt) and \
> + self._e(pkt) and \
> + self._f2(pkt) and \
> + self._h2(pkt) and \
> + self._i1(pkt)
> + elif self._state == RatpState.fin_wait:
> + self._c2(pkt) and \
> + self._d2(pkt) and \
> + self._e(pkt) and \
> + self._f3(pkt) and \
> + self._h3(pkt)
> + elif self._state == RatpState.last_ack:
> + self._c2(pkt) and \
> + self._d3(pkt) and \
> + self._e(pkt) and \
> + self._f3(pkt) and \
> + self._h4(pkt)
> + elif self._state == RatpState.closing:
> + self._c2(pkt) and \
> + self._d3(pkt) and \
> + self._e(pkt) and \
> + self._f3(pkt) and \
> + self._h5(pkt)
> + elif self._state == RatpState.time_wait:
> + self._d3(pkt) and \
> + self._e(pkt) and \
> + self._f3(pkt) and \
> + self._h6(pkt)
> + elif self._state == RatpState.closed:
> + self._g(pkt)
> +
> + def wait(self, deadline):
> + while deadline is None or deadline > monotonic():
> + pkt = self._read()
> + if pkt:
> + self._machine(pkt)
> + else:
> + self._check_rto()
> + self._check_time_wait()
> + if not self._retrans or self._rx_queue:
> + return
> +
> + def wait1(self, deadline):
> + while deadline is None or deadline > monotonic():
> + pkt = self._read()
> + if pkt:
> + self._machine(pkt)
> + else:
> + self._check_rto()
> + self._check_time_wait()
> + if not self._retrans:
> + return
> +
> + def listen(self):
> + logging.info("LISTEN")
> + self._state = RatpState.listen
> +
> + def connect(self, timeout=5.0):
> + deadline = monotonic() + timeout
> + logging.info("CONNECT")
> + self._retrans = None
> + syn = RatpPacket(flags='S')
> + syn.length = self._s_mdl
> + self._write(syn)
> + self._state = RatpState.syn_sent
> + self.wait(deadline)
> +
> + def send_one(self, data, eor=True, timeout=1.0):
> + deadline = monotonic() + timeout
> + logging.info("SEND_ONE (len=%i, eor=%r)", len(data), eor)
> + assert self._state == RatpState.established
> + assert self._retrans is None
> + snd = RatpPacket(flags='A')
> + snd.c_eor = eor
> + snd.c_sn = (self._s_sn + 1) % 2
> + snd.c_an = (self._r_sn + 1) % 2
> + snd.length = len(data)
> + snd.payload = data
> + self._write(snd)
> + self.wait1(deadline=None)
> +
> + def send(self, data, timeout=1.0):
> + logging.info("SEND (len=%i)", len(data))
> + while len(data) > 255:
> + self.send_one(data[:255], eor=False, timeout=timeout)
> + data = data[255:]
> + self.send_one(data, eor=True, timeout=timeout)
> +
> + def recv(self, timeout=1.0):
> + deadline = monotonic() + timeout
> +
> + assert self._state == RatpState.established
> + if self._rx_queue:
> + return self._rx_queue.pop(0)
> + self.wait(deadline)
> + if self._rx_queue:
> + return self._rx_queue.pop(0)
> +
> + def close(self, timeout=1.0):
> + deadline = monotonic() + timeout
> + logging.info("CLOSE")
> + if self._state == RatpState.established:
> + fin = RatpPacket(flags='FA') # FIXME: only F?
> + fin.c_sn = (self._s_sn + 1) % 2
> + fin.c_an = (self._r_sn + 1) % 2
> + self._write(fin)
> + self._state = RatpState.fin_wait
> + while deadline > monotonic() and not self._state == RatpState.time_wait:
> + self.wait(deadline)
> + while self._state == RatpState.time_wait:
> + self.wait(None)
> + if self._state == RatpState.closed:
> + logging.info("CLOSE: success")
> + else:
> + logging.info("CLOSE: failure")
> +
> +
> + def abort(self):
> + logging.info("ABORT")
> +
> + def status(self):
> + logging.info("STATUS")
> + return self._state
> +
> +
> +class SerialRatpConnection(RatpConnection):
> + def __init__(self, port):
> + super(SerialRatpConnection, self).__init__()
> + self.__port = port
> + self.__port.timeout = 0.01
> + self.__port.writeTimeout = None
> + self.__port.flushInput()
> +
> + def _write_raw(self, data):
> + if data:
> + logging.debug("-> %r", bytearray(data))
> + return self.__port.write(data)
> +
> + def _read_raw(self, size=1):
> + data = self.__port.read(size)
> + if data:
> + logging.debug("<- %r", bytearray(data))
> + return data
> diff --git a/scripts/remote/ratpfs.py b/scripts/remote/ratpfs.py
> new file mode 100644
> index 0000000..0333bf7
> --- /dev/null
> +++ b/scripts/remote/ratpfs.py
> @@ -0,0 +1,189 @@
> +#!/usr/bin/env python2
> +# -*- coding: utf-8 -*-
> +
> +from __future__ import absolute_import, division, print_function
> +
> +import logging
> +import os
> +import stat
> +import struct
> +from enum import IntEnum
> +
> +from .messages import BBPacketFS, BBPacketFSReturn
> +
> +class RatpFSType(IntEnum):
> + invalid = 0
> + mount_call = 1
> + mount_return = 2
> + readdir_call = 3
> + readdir_return = 4
> + stat_call = 5
> + stat_return = 6
> + open_call = 7
> + open_return = 8
> + read_call = 9
> + read_return = 10
> + write_call = 11
> + write_return = 12
> + close_call = 13
> + close_return = 14
> + truncate_call = 15
> + truncate_return = 16
> +
> +
> +class RatpFSError(ValueError):
> + pass
> +
> +
> +class RatpFSPacket(object):
> + def __init__(self, type=RatpFSType.invalid, payload="", raw=None):
> + if raw is not None:
> + type, = struct.unpack('!B', raw[:1])
> + self.type = RatpFSType(type)
> + self.payload = raw[1:]
> + else:
> + self.type = type
> + self.payload = payload
> +
> + def __repr__(self):
> + s = "%s(" % self.__class__.__name__
> + s += "TYPE=%i," % self.type
> + s += "PAYLOAD=%s)" % repr(self.payload)
> + return s
> +
> + def pack(self):
> + return struct.pack('!B', int(self.type))+self.payload
> +
> +
> +class RatpFSServer(object):
> + def __init__(self, path=None):
> + self.path = path
> + if path:
> + self.path = os.path.abspath(path)
> + self.next_handle = 1 # 0 is invalid
> + self.files = {}
> + self.mounted = False
> + logging.info("exporting: %s", self.path)
> +
> + def _alloc_handle(self):
> + handle = self.next_handle
> + self.next_handle += 1
> + return handle
> +
> + def _resolve(self, path):
> + components = path.split('/')
> + components = [x for x in components if x and x != '..']
> + return os.path.join(self.path, *components)
> +
> + def handle_stat(self, path):
> +
> + try:
> + logging.info("path: %r", path)
> + path = self._resolve(path)
> + logging.info("path1: %r", path)
> + s = os.stat(path)
> + except OSError as e:
> + return struct.pack('!BI', 0, e.errno)
> + if stat.S_ISREG(s.st_mode):
> + return struct.pack('!BI', 1, s.st_size)
> + elif stat.S_ISDIR(s.st_mode):
> + return struct.pack('!BI', 2, s.st_size)
> + else:
> + return struct.pack('!BI', 0, 0)
> +
> + def handle_open(self, params):
> + flags, = struct.unpack('!I', params[:4])
> + flags = flags & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR | os.O_CREAT |
> + os.O_TRUNC)
> + path = params[4:]
> + try:
> + f = os.open(self._resolve(path), flags, 0666)
> + except OSError as e:
> + return struct.pack('!II', 0, e.errno)
> + h = self._alloc_handle()
> + self.files[h] = f
> + size = os.lseek(f, 0, os.SEEK_END)
> + return struct.pack('!II', h, size)
> +
> + def handle_read(self, params):
> + h, pos, size = struct.unpack('!III', params)
> + f = self.files[h]
> + os.lseek(f, pos, os.SEEK_SET)
> + size = min(size, 4096)
> + return os.read(f, size)
> +
> + def handle_write(self, params):
> + h, pos = struct.unpack('!II', params[:8])
> + payload = params[8:]
> + f = self.files[h]
> + pos = os.lseek(f, pos, os.SEEK_SET)
> + assert os.write(f, payload) == len(payload)
> + return ""
> +
> + def handle_readdir(self, path):
> + res = ""
> + for x in os.listdir(self._resolve(path)):
> + res += x+'\0'
> + return res
> +
> + def handle_close(self, params):
> + h, = struct.unpack('!I', params[:4])
> + os.close(self.files.pop(h))
> + return ""
> +
> + def handle_truncate(self, params):
> + h, size = struct.unpack('!II', params)
> + f = self.files[h]
> + os.ftruncate(f, size)
> + return ""
> +
> + def handle(self, bbcall):
> + assert isinstance(bbcall, BBPacketFS)
> + logging.debug("bb-call: %s", bbcall)
> + fscall = RatpFSPacket(raw=bbcall.payload)
> + logging.info("fs-call: %s", fscall)
> +
> + if not self.path:
> + logging.warning("no filesystem exported")
> + fsreturn = RatpFSPacket(type=RatpFSType.invalid)
> + elif fscall.type == RatpFSType.mount_call:
> + self.mounted = True
> + fsreturn = RatpFSPacket(type=RatpFSType.mount_return)
> + elif not self.mounted:
> + logging.warning("filesystem not mounted")
> + fsreturn = RatpFSPacket(type=RatpFSType.invalid)
> + elif fscall.type == RatpFSType.readdir_call:
> + payload = self.handle_readdir(fscall.payload)
> + fsreturn = RatpFSPacket(type=RatpFSType.readdir_return,
> + payload=payload)
> + elif fscall.type == RatpFSType.stat_call:
> + payload = self.handle_stat(fscall.payload)
> + fsreturn = RatpFSPacket(type=RatpFSType.stat_return,
> + payload=payload)
> + elif fscall.type == RatpFSType.open_call:
> + payload = self.handle_open(fscall.payload)
> + fsreturn = RatpFSPacket(type=RatpFSType.open_return,
> + payload=payload)
> + elif fscall.type == RatpFSType.read_call:
> + payload = self.handle_read(fscall.payload)
> + fsreturn = RatpFSPacket(type=RatpFSType.read_return,
> + payload=payload)
> + elif fscall.type == RatpFSType.write_call:
> + payload = self.handle_write(fscall.payload)
> + fsreturn = RatpFSPacket(type=RatpFSType.write_return,
> + payload=payload)
> + elif fscall.type == RatpFSType.close_call:
> + payload = self.handle_close(fscall.payload)
> + fsreturn = RatpFSPacket(type=RatpFSType.close_return,
> + payload=payload)
> + elif fscall.type == RatpFSType.truncate_call:
> + payload = self.handle_truncate(fscall.payload)
> + fsreturn = RatpFSPacket(type=RatpFSType.truncate_return,
> + payload=payload)
> + else:
> + raise RatpFSError()
> +
> + logging.info("fs-return: %s", fsreturn)
> + bbreturn = BBPacketFSReturn(payload=fsreturn.pack())
> + logging.debug("bb-return: %s", bbreturn)
> + return bbreturn
> diff --git a/scripts/remote/threadstdio.py b/scripts/remote/threadstdio.py
> new file mode 100644
> index 0000000..db24989
> --- /dev/null
> +++ b/scripts/remote/threadstdio.py
> @@ -0,0 +1,47 @@
> +#!/usr/bin/python2
> +
> +import os
> +import sys
> +import termios
> +import atexit
> +from threading import Thread
> +from Queue import Queue, Empty
> +
> +class ConsoleInput(Thread):
> + def __init__(self, queue, exit='\x14'):
> + Thread.__init__(self)
> + self.daemon = True
> + self.q = queue
> + self._exit = exit
> + self.fd = sys.stdin.fileno()
> + old = termios.tcgetattr(self.fd)
> + new = termios.tcgetattr(self.fd)
> + new[3] = new[3] & ~termios.ICANON & ~termios.ECHO & ~termios.ISIG
> + new[6][termios.VMIN] = 1
> + new[6][termios.VTIME] = 0
> + termios.tcsetattr(self.fd, termios.TCSANOW, new)
> +
> + def cleanup():
> + termios.tcsetattr(self.fd, termios.TCSAFLUSH, old)
> + atexit.register(cleanup)
> +
> + def run(self):
> + while True:
> + c = os.read(self.fd, 1)
> + if c == self._exit:
> + self.q.put((self, None))
> + return
> + else:
> + self.q.put((self, c))
> +
> +if __name__ == "__main__":
> + q = Queue()
> + i = ConsoleInput(q)
> + i.start()
> + while True:
> + event = q.get(block=True)
> + src, c = event
> + if c == '\x04':
> + break
> + os.write(sys.stdout.fileno(), c.upper())
> +
> --
> 2.6.4
>
>
> _______________________________________________
> barebox mailing list
> barebox at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/barebox
More information about the barebox
mailing list