nu/src/bank_node/bank_node.py

110 lines
3.6 KiB
Python

import socket
import signal
import sys
import logging
from core.config import BankNodeConfig
from core.exceptions import ConfigError
from utils import setup_logger
from database.database_manager import DatabaseManager
from bank_node.bank_worker import BankWorker
class BankNode():
def __init__(self):
try:
self.config = BankNodeConfig()
setup_logger(self.config.verbosity)
self.logger = logging.getLogger(__name__)
self.logger.info("Config is valid")
self.logger.debug("Starting Bank Node")
self.__setup_signals()
self.database_manager = DatabaseManager()
self.socket_server = None
except ConfigError as e:
print(e)
self.exit_with_error()
def __setup_signals(self):
self.logger.debug("Setting up exit signal hooks")
# Not as clean as
# atexit.register(self.gracefully_exit)
# But it gives more control and is easier
# to understand in the output
# and looks better
# Handle C standard signals
signal.signal(signal.SIGTERM, self.gracefully_exit)
signal.signal(signal.SIGINT, self.gracefully_exit)
# Handle windows related signals
if sys.platform == "win32":
signal.signal(signal.CTRL_C_EVENT, self.gracefully_exit)
signal.signal(signal.CTRL_BREAK_EVENT, self.gracefully_exit)
signal.signal(signal.SIGBREAK, self.gracefully_exit)
def start(self):
try:
for port in range(self.config.scan_port_start, self.config.scan_port_end + 1):
self.logger.debug("Trying port %d", port)
try:
self.config.used_port = port
self.__start_server(port)
return
except socket.error as e:
if e.errno == 98: # Address is in use
self.logger.info("Port %d in use, trying next port", port)
else:
raise
self.logger.error("All ports are in use")
self.exit_with_error()
except socket.error as e:
if e.errno == 99: # Cannot assign to requested address
self.logger.critical("Cannot use the IP address %s", self.config.ip)
else:
self.logger.critical("Unknown error: %s", e)
self.exit_with_error()
def __start_server(self, port: int):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as socket_server:
socket_server.bind((self.config.ip, port))
socket_server.listen()
self.socket_server = socket_server
self.logger.info("Listening on %s:%s", self.config.ip, port)
while True:
client_socket, address = socket_server.accept()
self.logger.info("%s connected", address[0])
process = BankWorker(client_socket, address, self.config)
process.start()
def exit_with_error(self):
"""Exit the application with status of 1"""
sys.exit(1)
def gracefully_exit(self, signum, _):
"""Log the signal caught and exit with status 0"""
signal_name = signal.Signals(signum).name
self.logger.warning("Caught %s - cleaning up", signal_name)
self.cleanup()
self.logger.info("Exiting")
sys.exit(0)
def cleanup(self):
self.logger.debug("Closing socket server")
self.socket_server.close()
self.logger.debug("Closing database connection")
self.database_manager.cleanup()