diff --git a/README.md b/README.md index d7107b1..b78bfc0 100644 --- a/README.md +++ b/README.md @@ -56,15 +56,19 @@ General Usage: sudo python3 ca.py -v -c -h 1 -r 5 -t 10 -a dead:beef::1 ``` + ## ta.py This program provides full TA functionality for the node. This program should only be run on the designated DODAG node of the system (normally IPv6 address dead:beef::1 by convention). The program is generally executed as: ``` -sudo python3 ta.py -e -d [D] +sudo python3 ta.py -v -e -d [D] ``` | CLI Parameters | Description | |----------------|-------------| +| `-v` | Enable verbose mode | | `-e` | Enable AES encryption on packets | -| `-d` | Relative path from the calling directory to the SQLite3 database containing the `usages` table, in which the TA will store received usage data +| `-d [DB]` | Relative path from the calling directory to the SQLite3 database containing the `usages` table, in which the TA will store received usage data + +Ta.py handles interrupts gracefully, SIGKILL (2, ^C) will kill the program and close any open sockets. SIGQUIT (3, ^\\) will break the current iteration of the loop, this is useful for when SYNACKs are lost in the mesh and the receive socket livelocks (this appears to be a TCP 'bug' of sorts). Of course, any data currently in the socket will be lost. If not explicitly set using the CLI parameters, the database used is set to `dat/power-usages.db` (relative to the directory `ta.py` is called from). The sqlite3 database used by the TA **must** contain a table entitled "usages" which conforms to a predefined schema, given in [init.sql](https://github.com/carleton-smart-grid/smartgrid-comms/blob/master/startup/init.sql). Initial setup of the database can be done trivially using the provide `init.sql` file, given as: ``` @@ -88,6 +92,7 @@ sudo python cliReporter.py 0:1:2:3 192.168.1.50 ``` + ## udpping.py TODO @@ -180,3 +185,9 @@ Function Call | Precondition(s) | Postcondition(s) ## encryptiontool.py TODO @argRobertson + +Function Call | Precondition(s) | Postcondition(s) +--------------|-----------------|----------------- +... | ... | ... +... | ... | ... +... | ... | ... diff --git a/mesh-comms/ta.py b/mesh-comms/ta.py index 427d7b3..ee08566 100644 --- a/mesh-comms/ta.py +++ b/mesh-comms/ta.py @@ -4,9 +4,11 @@ # import external libraries +import signal import xml.etree.ElementTree as et import sqlite3 import sys +# import exception # import local py import tcpcomms @@ -19,11 +21,32 @@ VALID_PACKET = 16 DEFAULT_DB_PATH = 'dat/power-usages.db' +############################################################################### +# GRACEFUL SIGNAL HANDLING +############################################################################### +class Killer: + die = False + def __init__(self): + signal.signal(signal.SIGINT, self.terminate) + signal.signal(signal.SIGTERM, self.terminate) + signal.signal(signal.SIGQUIT, self.quit) + + def terminate(self, signum, frame): + printv("Terminate caught " + str(signum)) + self.die = True; + raise InterruptedError + + def quit(self, signum, frame): + printv("Quit caught " + str(signum)) + raise InterruptedError ############################################################################### # DECLARING FUNCTIONS ############################################################################### +def printv(string): + if verbose: + print(string) # save xml data to database def write(xml, curser, connection): @@ -67,6 +90,8 @@ def write(xml, curser, connection): # initialize parameters to default dbPath = DEFAULT_DB_PATH encryptOn = False +verbose = False +killer = Killer() # check for flags flags = sys.argv @@ -78,6 +103,8 @@ def write(xml, curser, connection): dbPath = str(flags.pop(0)) elif (flag == '-e'): encryptOn = True + elif (flag == '-v'): + verbose = True # setup server object server = tcpcomms.Server() @@ -91,9 +118,14 @@ def write(xml, curser, connection): tool = encryptiontool.SecurityTool() # receive-unpack-write loop -while(True): +while(killer.die != True): # receive and unpack data - packet = server.receive() + try: + packet = server.receive() + except InterruptedError: + print("Interrupted socket receive, continuing") # This is an error and as such is printed regardless of verbosity + continue + #if the packet is an RSA encrypted AES key if(len(packet[0]) == RSA_PACKET and encryptOn): tool.addAesKey(packet) @@ -106,8 +138,13 @@ def write(xml, curser, connection): try: data = packer.unpack(data) except Exception as err: - print('IMF unpack error: ', err) + print('IMF unpack error: ', err) # This is an error and as such is printed regardless of verbosity continue #save to SQLite3 server write(data, curser, connection) + + +# Cleanup - any future cleanup can go here. +del(server) # This doesn't necessarily have to be done, but it doesn't hurt. +print("Exiting") \ No newline at end of file diff --git a/mesh-comms/tcpcomms.py b/mesh-comms/tcpcomms.py index 2c80a7b..8e26f79 100644 --- a/mesh-comms/tcpcomms.py +++ b/mesh-comms/tcpcomms.py @@ -74,7 +74,7 @@ def setup(self): # socket setup printv('Binding Socket on port', PORT, '...') scope_id = socket.if_nametoindex('lowpan0') - #scope_id = socket.AF_INET + # scope_id = socket.AF_INET self.serverSocket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, 0) self.serverSocket.bind((HOST, PORT, 0, scope_id)) #self.serverSocket.bind((HOST, PORT)) @@ -86,7 +86,7 @@ def setup(self): def teardown(self): if (self.serverSocket != None): print('Closing socket...') - self.serverSocket.close + self.serverSocket.shutdown(socket.SHUT_RDWR) # Shutdown reads and writes # recieve data over multiple transactions