From 7e9d608530dac8533771c6a9c07b7f3b451cf34f Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Fri, 24 Sep 2021 16:42:31 +0200 Subject: [PATCH] Improved shutdown handling for local shared instances --- RNS/Interfaces/LocalInterface.py | 32 ++++++++++++++++++++++++++++++++ RNS/Interfaces/TCPInterface.py | 18 +++++++++++++++--- RNS/Transport.py | 3 +++ 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/RNS/Interfaces/LocalInterface.py b/RNS/Interfaces/LocalInterface.py index b1629f6..e2389f2 100644 --- a/RNS/Interfaces/LocalInterface.py +++ b/RNS/Interfaces/LocalInterface.py @@ -36,6 +36,8 @@ class LocalClientInterface(Interface): self.target_port = None self.socket = connected_socket + self.is_connected_to_shared_instance = False + elif target_port != None: self.receives = True self.target_ip = "127.0.0.1" @@ -116,6 +118,25 @@ class LocalClientInterface(Interface): RNS.log("Tearing down "+str(self), RNS.LOG_ERROR) self.teardown() + def detach(self): + if self.socket != None: + if hasattr(self.socket, "close"): + if callable(self.socket.close): + RNS.log("Detaching "+str(self), RNS.LOG_DEBUG) + self.detached = True + + try: + self.socket.shutdown(socket.SHUT_RDWR) + except Exception as e: + RNS.log("Error while shutting down socket for "+str(self)+": "+str(e)) + + try: + self.socket.close() + except Exception as e: + RNS.log("Error while closing socket for "+str(self)+": "+str(e)) + + self.socket = None + def teardown(self, nowarning=False): self.online = False self.OUT = False @@ -132,6 +153,15 @@ class LocalClientInterface(Interface): if RNS.Reticulum.panic_on_interface_error: RNS.panic() + if self.is_connected_to_shared_instance: + # TODO: Maybe add automatic recovery here. + # Needs thinking through, since user needs + # to now that all connectivity has been cut + # while service is recovering. Better for + # now to take down entire stack. + RNS.log("Lost connection to local shared RNS instance. Exiting now.", RNS.LOG_CRITICAL) + RNS.panic() + def __str__(self): return "LocalInterface["+str(self.target_port)+"]" @@ -158,6 +188,8 @@ class LocalServerInterface(Interface): self.is_local_shared_instance = True address = (self.bind_ip, self.bind_port) + + ThreadingTCPServer.allow_reuse_address = True self.server = ThreadingTCPServer(address, handlerFactory(self.incoming_connection)) thread = threading.Thread(target=self.server.serve_forever) diff --git a/RNS/Interfaces/TCPInterface.py b/RNS/Interfaces/TCPInterface.py index d39b26c..b27183d 100644 --- a/RNS/Interfaces/TCPInterface.py +++ b/RNS/Interfaces/TCPInterface.py @@ -45,6 +45,7 @@ class TCPClientInterface(Interface): self.owner = owner self.writing = False self.online = False + self.detached = False if max_reconnect_tries == None: self.max_reconnect_tries = TCPClientInterface.RECONNECT_MAX_TRIES @@ -101,8 +102,18 @@ class TCPClientInterface(Interface): if hasattr(self.socket, "close"): if callable(self.socket.close): RNS.log("Detaching "+str(self), RNS.LOG_DEBUG) - self.socket.shutdown(socket.SHUT_RDWR) - self.socket.close() + self.detached = True + + try: + self.socket.shutdown(socket.SHUT_RDWR) + except Exception as e: + RNS.log("Error while shutting down socket for "+str(self)+": "+str(e)) + + try: + self.socket.close() + except Exception as e: + RNS.log("Error while closing socket for "+str(self)+": "+str(e)) + self.socket = None def connect(self, initial=False): @@ -216,7 +227,7 @@ class TCPClientInterface(Interface): data_buffer = data_buffer+bytes([byte]) else: self.online = False - if self.initiator: + if self.initiator and not self.detached: RNS.log("TCP socket for "+str(self)+" was closed, attempting to reconnect...", RNS.LOG_WARNING) self.reconnect() else: @@ -285,6 +296,7 @@ class TCPServerInterface(Interface): self.owner = owner address = (self.bind_ip, self.bind_port) + ThreadingTCPServer.allow_reuse_address = True self.server = ThreadingTCPServer(address, handlerFactory(self.incoming_connection)) diff --git a/RNS/Transport.py b/RNS/Transport.py index 8b7af39..85932ff 100755 --- a/RNS/Transport.py +++ b/RNS/Transport.py @@ -1427,6 +1427,9 @@ class Transport: for interface in Transport.interfaces: interface.detach() + for interface in Transport.local_client_interfaces: + interface.detach() + @staticmethod def exit_handler():