110 lines
3.6 KiB
Python
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()
|