mirror of
https://github.com/craigerl/aprsd.git
synced 2025-09-03 21:57:47 -04:00
Updated APRSIS driver
This patch adds an override to _connect to set some more socket keepalive options.
This commit is contained in:
parent
3b57e7597d
commit
1606585d41
@ -1,6 +1,7 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
import select
|
import select
|
||||||
|
import socket
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
import aprslib
|
import aprslib
|
||||||
@ -18,7 +19,7 @@ from aprslib.exceptions import (
|
|||||||
import aprsd
|
import aprsd
|
||||||
from aprsd.packets import core
|
from aprsd.packets import core
|
||||||
|
|
||||||
LOG = logging.getLogger("APRSD")
|
LOG = logging.getLogger('APRSD')
|
||||||
|
|
||||||
|
|
||||||
class Aprsdis(aprslib.IS):
|
class Aprsdis(aprslib.IS):
|
||||||
@ -31,7 +32,7 @@ class Aprsdis(aprslib.IS):
|
|||||||
aprsd_keepalive = datetime.datetime.now()
|
aprsd_keepalive = datetime.datetime.now()
|
||||||
|
|
||||||
# Which server we are connected to?
|
# Which server we are connected to?
|
||||||
server_string = "None"
|
server_string = 'None'
|
||||||
|
|
||||||
# timeout in seconds
|
# timeout in seconds
|
||||||
select_timeout = 1
|
select_timeout = 1
|
||||||
@ -39,10 +40,10 @@ class Aprsdis(aprslib.IS):
|
|||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.thread_stop = True
|
self.thread_stop = True
|
||||||
LOG.warning("Shutdown Aprsdis client.")
|
LOG.warning('Shutdown Aprsdis client.')
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
LOG.warning("Closing Aprsdis client.")
|
LOG.warning('Closing Aprsdis client.')
|
||||||
super().close()
|
super().close()
|
||||||
|
|
||||||
@wrapt.synchronized(lock)
|
@wrapt.synchronized(lock)
|
||||||
@ -54,6 +55,57 @@ class Aprsdis(aprslib.IS):
|
|||||||
"""If the connection is alive or not."""
|
"""If the connection is alive or not."""
|
||||||
return self._connected
|
return self._connected
|
||||||
|
|
||||||
|
def _connect(self):
|
||||||
|
"""
|
||||||
|
Attemps connection to the server
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.logger.info(
|
||||||
|
'Attempting connection to %s:%s', self.server[0], self.server[1]
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._open_socket()
|
||||||
|
|
||||||
|
peer = self.sock.getpeername()
|
||||||
|
|
||||||
|
self.logger.info('Connected to %s', str(peer))
|
||||||
|
|
||||||
|
# 5 second timeout to receive server banner
|
||||||
|
self.sock.setblocking(1)
|
||||||
|
self.sock.settimeout(5)
|
||||||
|
|
||||||
|
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
||||||
|
# MACOS doesn't have TCP_KEEPIDLE
|
||||||
|
if hasattr(socket, 'TCP_KEEPIDLE'):
|
||||||
|
self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 1)
|
||||||
|
self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 3)
|
||||||
|
self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 5)
|
||||||
|
|
||||||
|
banner = self.sock.recv(512)
|
||||||
|
if is_py3:
|
||||||
|
banner = banner.decode('latin-1')
|
||||||
|
|
||||||
|
if banner[0] == '#':
|
||||||
|
self.logger.debug('Banner: %s', banner.rstrip())
|
||||||
|
else:
|
||||||
|
raise ConnectionError('invalid banner from server')
|
||||||
|
|
||||||
|
except ConnectionError as e:
|
||||||
|
self.logger.error(str(e))
|
||||||
|
self.close()
|
||||||
|
raise
|
||||||
|
except (socket.error, socket.timeout) as e:
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
self.logger.error('Socket error: %s' % str(e))
|
||||||
|
if str(e) == 'timed out':
|
||||||
|
raise ConnectionError('no banner from server') from e
|
||||||
|
else:
|
||||||
|
raise ConnectionError(e) from e
|
||||||
|
|
||||||
|
self._connected = True
|
||||||
|
|
||||||
def _socket_readlines(self, blocking=False):
|
def _socket_readlines(self, blocking=False):
|
||||||
"""
|
"""
|
||||||
Generator for complete lines, received from the server
|
Generator for complete lines, received from the server
|
||||||
@ -61,12 +113,12 @@ class Aprsdis(aprslib.IS):
|
|||||||
try:
|
try:
|
||||||
self.sock.setblocking(0)
|
self.sock.setblocking(0)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
self.logger.error(f"socket error when setblocking(0): {str(e)}")
|
self.logger.error(f'socket error when setblocking(0): {str(e)}')
|
||||||
raise aprslib.ConnectionDrop("connection dropped")
|
raise aprslib.ConnectionDrop('connection dropped') from e
|
||||||
|
|
||||||
while not self.thread_stop:
|
while not self.thread_stop:
|
||||||
short_buf = b""
|
short_buf = b''
|
||||||
newline = b"\r\n"
|
newline = b'\r\n'
|
||||||
|
|
||||||
# set a select timeout, so we get a chance to exit
|
# set a select timeout, so we get a chance to exit
|
||||||
# when user hits CTRL-C
|
# when user hits CTRL-C
|
||||||
@ -91,11 +143,11 @@ class Aprsdis(aprslib.IS):
|
|||||||
# We could just not be blocking, so empty is expected
|
# We could just not be blocking, so empty is expected
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
self.logger.error("socket.recv(): returned empty")
|
self.logger.error('socket.recv(): returned empty')
|
||||||
raise aprslib.ConnectionDrop("connection dropped")
|
raise aprslib.ConnectionDrop('connection dropped')
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
# self.logger.error("socket error on recv(): %s" % str(e))
|
# self.logger.error("socket error on recv(): %s" % str(e))
|
||||||
if "Resource temporarily unavailable" in str(e):
|
if 'Resource temporarily unavailable' in str(e):
|
||||||
if not blocking:
|
if not blocking:
|
||||||
if len(self.buf) == 0:
|
if len(self.buf) == 0:
|
||||||
break
|
break
|
||||||
@ -111,22 +163,22 @@ class Aprsdis(aprslib.IS):
|
|||||||
"""
|
"""
|
||||||
Sends login string to server
|
Sends login string to server
|
||||||
"""
|
"""
|
||||||
login_str = "user {0} pass {1} vers github.com/craigerl/aprsd {3}{2}\r\n"
|
login_str = 'user {0} pass {1} vers Python-APRSD {3}{2}\r\n'
|
||||||
login_str = login_str.format(
|
login_str = login_str.format(
|
||||||
self.callsign,
|
self.callsign,
|
||||||
self.passwd,
|
self.passwd,
|
||||||
(" filter " + self.filter) if self.filter != "" else "",
|
(' filter ' + self.filter) if self.filter != '' else '',
|
||||||
aprsd.__version__,
|
aprsd.__version__,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.logger.debug("Sending login information")
|
self.logger.debug('Sending login information')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._sendall(login_str)
|
self._sendall(login_str)
|
||||||
self.sock.settimeout(5)
|
self.sock.settimeout(5)
|
||||||
test = self.sock.recv(len(login_str) + 100)
|
test = self.sock.recv(len(login_str) + 100)
|
||||||
if is_py3:
|
if is_py3:
|
||||||
test = test.decode("latin-1")
|
test = test.decode('latin-1')
|
||||||
test = test.rstrip()
|
test = test.rstrip()
|
||||||
|
|
||||||
self.logger.debug("Server: '%s'", test)
|
self.logger.debug("Server: '%s'", test)
|
||||||
@ -134,26 +186,26 @@ class Aprsdis(aprslib.IS):
|
|||||||
if not test:
|
if not test:
|
||||||
raise LoginError(f"Server Response Empty: '{test}'")
|
raise LoginError(f"Server Response Empty: '{test}'")
|
||||||
|
|
||||||
_, _, callsign, status, e = test.split(" ", 4)
|
_, _, callsign, status, e = test.split(' ', 4)
|
||||||
s = e.split(",")
|
s = e.split(',')
|
||||||
if len(s):
|
if len(s):
|
||||||
server_string = s[0].replace("server ", "")
|
server_string = s[0].replace('server ', '')
|
||||||
else:
|
else:
|
||||||
server_string = e.replace("server ", "")
|
server_string = e.replace('server ', '')
|
||||||
|
|
||||||
if callsign == "":
|
if callsign == '':
|
||||||
raise LoginError("Server responded with empty callsign???")
|
raise LoginError('Server responded with empty callsign???')
|
||||||
if callsign != self.callsign:
|
if callsign != self.callsign:
|
||||||
raise LoginError(f"Server: {test}")
|
raise LoginError(f'Server: {test}')
|
||||||
if status != "verified," and self.passwd != "-1":
|
if status != 'verified,' and self.passwd != '-1':
|
||||||
raise LoginError("Password is incorrect")
|
raise LoginError('Password is incorrect')
|
||||||
|
|
||||||
if self.passwd == "-1":
|
if self.passwd == '-1':
|
||||||
self.logger.info("Login successful (receive only)")
|
self.logger.info('Login successful (receive only)')
|
||||||
else:
|
else:
|
||||||
self.logger.info("Login successful")
|
self.logger.info('Login successful')
|
||||||
|
|
||||||
self.logger.info(f"Connected to {server_string}")
|
self.logger.info(f'Connected to {server_string}')
|
||||||
self.server_string = server_string
|
self.server_string = server_string
|
||||||
|
|
||||||
except LoginError as e:
|
except LoginError as e:
|
||||||
@ -164,7 +216,7 @@ class Aprsdis(aprslib.IS):
|
|||||||
self.close()
|
self.close()
|
||||||
self.logger.error(f"Failed to login '{e}'")
|
self.logger.error(f"Failed to login '{e}'")
|
||||||
self.logger.exception(e)
|
self.logger.exception(e)
|
||||||
raise LoginError("Failed to login")
|
raise LoginError('Failed to login') from e
|
||||||
|
|
||||||
def consumer(self, callback, blocking=True, immortal=False, raw=False):
|
def consumer(self, callback, blocking=True, immortal=False, raw=False):
|
||||||
"""
|
"""
|
||||||
@ -180,21 +232,21 @@ class Aprsdis(aprslib.IS):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if not self._connected:
|
if not self._connected:
|
||||||
raise ConnectionError("not connected to a server")
|
raise ConnectionError('not connected to a server')
|
||||||
|
|
||||||
line = b""
|
line = b''
|
||||||
|
|
||||||
while True and not self.thread_stop:
|
while True and not self.thread_stop:
|
||||||
try:
|
try:
|
||||||
for line in self._socket_readlines(blocking):
|
for line in self._socket_readlines(blocking):
|
||||||
if line[0:1] != b"#":
|
if line[0:1] != b'#':
|
||||||
self.aprsd_keepalive = datetime.datetime.now()
|
self.aprsd_keepalive = datetime.datetime.now()
|
||||||
if raw:
|
if raw:
|
||||||
callback(line)
|
callback(line)
|
||||||
else:
|
else:
|
||||||
callback(self._parse(line))
|
callback(self._parse(line))
|
||||||
else:
|
else:
|
||||||
self.logger.debug("Server: %s", line.decode("utf8"))
|
self.logger.debug('Server: %s', line.decode('utf8'))
|
||||||
self.aprsd_keepalive = datetime.datetime.now()
|
self.aprsd_keepalive = datetime.datetime.now()
|
||||||
except ParseError as exp:
|
except ParseError as exp:
|
||||||
self.logger.log(
|
self.logger.log(
|
||||||
@ -211,7 +263,7 @@ class Aprsdis(aprslib.IS):
|
|||||||
exp.packet,
|
exp.packet,
|
||||||
)
|
)
|
||||||
except LoginError as exp:
|
except LoginError as exp:
|
||||||
self.logger.error("%s: %s", exp.__class__.__name__, exp)
|
self.logger.error('%s: %s', exp.__class__.__name__, exp)
|
||||||
except (KeyboardInterrupt, SystemExit):
|
except (KeyboardInterrupt, SystemExit):
|
||||||
raise
|
raise
|
||||||
except (ConnectionDrop, ConnectionError):
|
except (ConnectionDrop, ConnectionError):
|
||||||
@ -227,7 +279,7 @@ class Aprsdis(aprslib.IS):
|
|||||||
except StopIteration:
|
except StopIteration:
|
||||||
break
|
break
|
||||||
except Exception:
|
except Exception:
|
||||||
self.logger.error("APRS Packet: %s", line)
|
self.logger.error('APRS Packet: %s', line)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
if not blocking:
|
if not blocking:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user