From 59d5f61d9001085df5b74cabb6cfc09efdbdf4be Mon Sep 17 00:00:00 2001 From: Ian Renton Date: Sat, 19 Sep 2026 07:55:12 +0100 Subject: [PATCH] Potential fix for an issue where the telnet client was reconnecting right at the same time we try to shut down spothole, causing the stop() method to close one telnet object but then a new one is created and read from anyway. --- providers/spot/dxcluster.py | 17 +++++++++++++---- providers/spot/rbn.py | 17 +++++++++++++---- templates/add_spot.html | 2 +- templates/alerts.html | 2 +- templates/bands.html | 4 ++-- templates/base.html | 10 +++++----- templates/conditions.html | 2 +- templates/map.html | 4 ++-- templates/spots.html | 4 ++-- templates/status.html | 2 +- 10 files changed, 41 insertions(+), 23 deletions(-) diff --git a/providers/spot/dxcluster.py b/providers/spot/dxcluster.py index eb537df..7ff26bb 100644 --- a/providers/spot/dxcluster.py +++ b/providers/spot/dxcluster.py @@ -1,7 +1,7 @@ import logging import re from datetime import datetime -from threading import Event, Thread +from threading import Event, Lock, Thread import pytz import telnetlib3 @@ -40,6 +40,7 @@ class DXCluster(SpotProvider): self._LINE_PATTERN_ALLOW_RBN if self._allow_rbn_spots else self._LINE_PATTERN_EXCLUDE_RBN ) self._telnet = None + self._telnet_lock = Lock() self._thread = None self._stop_event = Event() @@ -49,8 +50,9 @@ class DXCluster(SpotProvider): def stop(self): self._stop_event.set() - if self._telnet: - self._telnet.close() + with self._telnet_lock: + if self._telnet: + self._telnet.close() if self._thread: self._thread.join(timeout=15) if self._thread.is_alive(): @@ -63,7 +65,14 @@ class DXCluster(SpotProvider): try: self.status = "Connecting" logger.info(f"DX Cluster {self._hostname} connecting...") - self._telnet = telnetlib3.Telnet(self._hostname, self._port) + new_telnet = telnetlib3.Telnet(self._hostname, self._port) + with self._telnet_lock: + self._telnet = new_telnet + if self._stop_event.is_set(): + # stop() was called while we were connecting, close the connection rather than trying to + # read when we know it won't work + new_telnet.close() + break self._telnet.read_until(self._login_prompt.encode("latin-1")) self._telnet.write(f"{self._login_callsign}\n".encode("latin-1")) connected = True diff --git a/providers/spot/rbn.py b/providers/spot/rbn.py index 7923b9e..28b3261 100644 --- a/providers/spot/rbn.py +++ b/providers/spot/rbn.py @@ -1,7 +1,7 @@ import logging import re from datetime import datetime -from threading import Event, Thread +from threading import Event, Lock, Thread import pytz import telnetlib3 @@ -29,6 +29,7 @@ class RBN(SpotProvider): super().__init__(name, provider_config) self._port = provider_config["port"] self._telnet = None + self._telnet_lock = Lock() self._thread = None self._stop_event = Event() @@ -38,8 +39,9 @@ class RBN(SpotProvider): def stop(self): self._stop_event.set() - if self._telnet: - self._telnet.close() + with self._telnet_lock: + if self._telnet: + self._telnet.close() if self._thread: self._thread.join(timeout=15) if self._thread.is_alive(): @@ -52,7 +54,14 @@ class RBN(SpotProvider): try: self.status = "Connecting" logger.info(f"RBN port {self._port!s} connecting...") - self._telnet = telnetlib3.Telnet("telnet.reversebeacon.net", self._port) + new_telnet = telnetlib3.Telnet("telnet.reversebeacon.net", self._port) + with self._telnet_lock: + self._telnet = new_telnet + if self._stop_event.is_set(): + # stop() was called while we were connecting, close the connection rather than trying to + # read when we know it won't work + new_telnet.close() + break self._telnet.read_until("Please enter your call: ".encode("latin-1")) self._telnet.write(f"{SERVER_OWNER_CALLSIGN}\n".encode("latin-1")) connected = True diff --git a/templates/add_spot.html b/templates/add_spot.html index 1ac1058..b629870 100644 --- a/templates/add_spot.html +++ b/templates/add_spot.html @@ -77,7 +77,7 @@ - + diff --git a/templates/alerts.html b/templates/alerts.html index fed4253..a3b58e0 100644 --- a/templates/alerts.html +++ b/templates/alerts.html @@ -83,7 +83,7 @@ - + diff --git a/templates/bands.html b/templates/bands.html index fd234cc..dc7a4df 100644 --- a/templates/bands.html +++ b/templates/bands.html @@ -76,8 +76,8 @@ - - + + diff --git a/templates/base.html b/templates/base.html index 456bb53..75a8643 100644 --- a/templates/base.html +++ b/templates/base.html @@ -1,6 +1,6 @@ {% extends "skeleton.html" %} {% block head_extra %} - + @@ -16,10 +16,10 @@ window.fetchEventSource = fetchEventSource; - - - - + + + + {% end %} {% block body %}
diff --git a/templates/conditions.html b/templates/conditions.html index 550704d..7f5c5c4 100644 --- a/templates/conditions.html +++ b/templates/conditions.html @@ -284,7 +284,7 @@
- + diff --git a/templates/map.html b/templates/map.html index 622dd7b..286f01c 100644 --- a/templates/map.html +++ b/templates/map.html @@ -113,8 +113,8 @@ const CARTODB_API_KEY = "{{ web_ui_options.get('cartodb_api_key', '') }}"; - - + + diff --git a/templates/spots.html b/templates/spots.html index b0f6620..aff599b 100644 --- a/templates/spots.html +++ b/templates/spots.html @@ -125,8 +125,8 @@ - - + + diff --git a/templates/status.html b/templates/status.html index a3b190e..0513b7b 100644 --- a/templates/status.html +++ b/templates/status.html @@ -96,7 +96,7 @@ - +