summaryrefslogtreecommitdiff
path: root/server
diff options
context:
space:
mode:
authorJeffrey Armstrong <jeffrey.armstrong@approximatrix.com>2015-10-09 10:34:49 -0400
committerJeffrey Armstrong <jeffrey.armstrong@approximatrix.com>2015-10-09 10:34:49 -0400
commit8b756e368a33a02d7783053b84bc1e5b50419ff0 (patch)
tree658a81b96d4522fe3b7e0b33661dc265dbc968e8 /server
downloadrback-master.tar.gz
rback-master.zip
Initial commitHEADmaster
Diffstat (limited to 'server')
-rw-r--r--server/rback.py179
-rw-r--r--server/readme.txt44
2 files changed, 223 insertions, 0 deletions
diff --git a/server/rback.py b/server/rback.py
new file mode 100644
index 0000000..5a21669
--- /dev/null
+++ b/server/rback.py
@@ -0,0 +1,179 @@
+ # Serial Backup Server
+ # Copyright © 2015 Jeffrey Armstrong <jeff@rainbow-100.com>
+ # http://jeff.rainbow-100.com/
+ #
+ # This program is free software: you can redistribute it and/or modify
+ # it under the terms of the GNU General Public License as published by
+ # the Free Software Foundation, either version 3 of the License, or
+ # (at your option) any later version.
+ #
+ # This program is distributed in the hope that it will be useful
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ # GNU General Public License for more details.
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import time
+import serial
+import argparse
+import os
+import os.path
+
+DEFAULT_PORT = 19200
+DEFAULT_DEV = '/dev/ttyUSB0'
+
+OK = "OK"
+RETRY = "RETRY"
+ABORT = "ABORT"
+
+def respond(ser, msg):
+ """Writes a response message to the client"""
+ ser.write(msg)
+ ser.write(";")
+
+def fletcher16(data):
+ """Computes the Fletcher 16 checksum of a data stream"""
+ c0 = 0
+ c1 = 0
+
+ for b in data:
+ c0 = (c0 + ord(b))
+ c1 = (c1 + c0)
+
+ cb0 = 255 - ((c0+c1) % 255)
+ cb1 = 255 - ((c0 + cb0) % 255)
+
+ return cb0, cb1
+
+def receive_file(ser, fp):
+ """Receives a file block from the client, determines its validity,
+ and responds accordingly."""
+
+ file_contents = b''
+
+ length = ord(ser.read())
+ err = 0
+
+ while length > 0:
+ #print(" receiving {0} bytes".format(length))
+ bytes = b''
+ for i in range(0, length):
+ bytes = bytes + ser.read()
+
+ fl1 = ser.read()
+ fl2 = ser.read()
+
+ v1, v2 = fletcher16(bytes)
+
+ # print("Fletcher16 - Rainbow({0}, {1}) Python({2}, {3})".format(ord(fl1), ord(fl2), v1, v2))
+
+ if (v1 == ord(fl1) and v2 == ord(fl2)):
+ fp.write(bytes)
+ respond(ser, OK)
+ elif err < 10:
+ respond(ser, RETRY)
+ err = err + 1
+ if err == 1:
+ fp.write(bytes)
+ print(" block retry {0}...".format(err))
+ else:
+ respond(ser, ABORT)
+ print(" block abort")
+ break
+
+ length = ord(ser.read())
+
+def readtext(ser, bytes=1):
+ """Reads a string of bytes from the serial port and converts
+ them to a string if necessary (i.e. Python 3)"""
+ b = ser.read(bytes)
+ try:
+ return str(b,'ascii')
+ except TypeError:
+ return b
+
+def init(ser):
+ """Waits for an initialization message from the client"""
+ print("Waiting for 'ONLINE'...")
+ while ser.inWaiting() < 6:
+ time.sleep(1)
+
+ x = readtext(ser, 6)
+ if x == 'ONLINE':
+ print("Received 'ONLINE' message. Awaiting files...")
+ return True
+
+ return False
+
+def readfilename(ser):
+ """Reads the next filename from the client"""
+ fname = b''
+
+ b = ser.read()
+ while b != b';':
+ fname = fname + b
+ b = ser.read()
+
+ try:
+ return str(fname, 'ascii')
+ except TypeError:
+ return fname
+
+def prepfile(filename):
+ """Returns an open file for writing based on the
+ reported DOS name"""
+
+ newfile = filename.replace(':', '')
+ if os.sep != '\\':
+ newfile = newfile.replace('\\', os.sep)
+
+ dirs = newfile.split(os.sep)
+ fullpath = ''
+ for d in dirs[:-1]:
+ fullpath = os.path.join(fullpath,d)
+ if not os.path.exists(fullpath):
+ os.mkdir(fullpath)
+
+ return open(newfile, "w")
+
+def main_transfer(ser):
+ """Main program loop"""
+
+ # First wait for the online signal
+ if not init(ser):
+ print("Initialize failed. Quitting.")
+ return
+
+ while True:
+ fname = readfilename(ser)
+ fp = prepfile(fname)
+ if fp is None:
+ break
+
+ print("Receiving {0}".format(fname))
+ respond(ser, OK)
+ receive_file(ser, fp)
+ fp.close()
+
+def main(port='/dev/ttyUSB0', speed=19200):
+ """Opens the serial port and starts the transfer"""
+
+ ser = serial.Serial(port, speed)
+
+ main_transfer(ser)
+
+ ser.close()
+
+if __name__ == '__main__':
+
+ parser = argparse.ArgumentParser()
+
+ parser.add_argument('-p', '--port', dest='port', default='/dev/ttyUSB0')
+ parser.add_argument('-s', '--speed', dest='speed', type=int, default=19200)
+
+ res = parser.parse_args()
+
+ main(port=res.port, speed=res.speed)
+
diff --git a/server/readme.txt b/server/readme.txt
new file mode 100644
index 0000000..ae1b39e
--- /dev/null
+++ b/server/readme.txt
@@ -0,0 +1,44 @@
+Serial Backup Server
+====================
+
+This Python software acts as a server for receiving files via the
+included client program.
+
+Requirements
+------------
+
+The server software requires pyserial to be installed:
+
+ https://github.com/pyserial/pyserial
+
+The server software itself should run fine on both Python 2.7 and Python
+3.3 or higher.
+
+Executing
+---------
+
+The server software can be started simply with the command:
+
+ python rback.py
+
+The command above will start the server listening on port /dev/ttyUSB0
+with a speed of 19200 bps. Alternatively, port and speed can be
+specified:
+
+ python rback.py -p /dev/ttyACM0 -s 9600
+
+The above command is obvious.
+
+The server should be started before the client since it waits for the
+'ONLINE' indicator from the client before receiving files.
+
+The program will store transfered files in the client systems directory
+tree within the current directory. For example, if the client sends:
+
+ E:\MSDOS\EDLIN.EXE
+
+the server will store the file in:
+
+ <current directory>/E/MSDOS/EDLIN.EXE
+
+The server software will overwrite anything already present.