@ -34,6 +34,7 @@ import shutil
import ssl
import threading
import time
from collections . abc import Callable
from pathlib import Path
from typing import Any
from urllib . request import urlopen
@ -209,20 +210,21 @@ class DefaultAliasLoader(AliasLoader):
sub_file = aliases . get ( " SUBSCRIBER_FILE " , " subscriber_ids.json " )
tgid_file = aliases . get ( " TGID_FILE " , " talkgroup_ids.json " )
server_file = aliases . get ( " SERVER_ID_FILE " , " server_ids.tsv " )
peer_ids = self . _load_ id_dict_ with_backup(
path , peer_file , checksums . get ( " peer_ids " ) , " peer_ids " ,
peer_ids = self . _load_ with_backup(
path , peer_file , checksums . get ( " peer_ids " ) , " peer_ids " , self . _load_id_json ,
)
subscriber_ids = self . _load_ id_dict_ with_backup(
path , sub_file , checksums . get ( " subscriber_ids " ) , " subscriber_ids " ,
subscriber_ids = self . _load_ with_backup(
path , sub_file , checksums . get ( " subscriber_ids " ) , " subscriber_ids " , self . _load_id_json ,
)
talkgroup_ids = self . _load_ id_dict_ with_backup(
path , tgid_file , checksums . get ( " talkgroup_ids " ) , " talkgroup_ids " ,
talkgroup_ids = self . _load_ with_backup(
path , tgid_file , checksums . get ( " talkgroup_ids " ) , " talkgroup_ids " , self . _load_id_json ,
)
local_subscriber_ids = self . _load_id_json (
path / aliases . get ( " LOCAL_SUBSCRIBER_FILE " , " subscriber_ids.json " )
)
server_ids = self . _load_server_tsv_with_backup (
path , server_file , checksums . get ( " server_ids " ) ,
server_ids = self . _load_with_backup (
path , server_file , checksums . get ( " server_ids " ) , " server_ids " ,
lambda f : self . _load_server_tsv ( f . parent , f . name ) ,
)
return ( peer_ids , subscriber_ids , talkgroup_ids , local_subscriber_ids , server_ids , checksums )
@ -308,24 +310,25 @@ class DefaultAliasLoader(AliasLoader):
logger . warning ( " (ALIAS) ID ALIAS MAPPER: %s could not be read: %s " , file_name , err )
return { }
def _load_ id_dict_ with_backup(
def _load_ with_backup(
self ,
path : Path ,
file_name : str ,
expected_checksum : str | None ,
name : str ,
) - > dict [ int , str ] :
""" Legacy mk_aliases peer/subscriber/tgid load with .bak fallback. """
parse : Callable [ [ Path ] , dict ] ,
) - > dict :
""" Legacy mk_aliases load with .bak fallback, for whatever `parse` reads. """
full = path / file_name
bak = path / f " { file_name } .bak "
stamp = _file_stamp ( full )
remembered = self . _parsed . get ( file_name )
if stamp is not None and remembered is not None and remembered [ 0 ] == stamp :
return remembered [ 1 ]
result : dict [ int , str ] = { }
result : dict = { }
loaded_from_primary = False
def _load_verified ( target : Path ) - > dict [int , str ] :
def _load_verified ( target : Path ) - > dict :
if not target . is_file ( ) :
# Raise (not return {}) so the caller falls back to .bak below instead of
# silently ending up with an empty dictionary when the primary file is
@ -334,7 +337,7 @@ class DefaultAliasLoader(AliasLoader):
if expected_checksum :
if _blake2bsum ( target ) != expected_checksum :
raise ValueError ( " bad checksum " )
loaded = self . _load_id_json ( target )
loaded = parse ( target )
if not loaded :
raise ValueError ( " empty or invalid dictionary data " )
return loaded
@ -351,7 +354,7 @@ class DefaultAliasLoader(AliasLoader):
)
if bak . is_file ( ) :
try :
result = self . _load_id_json ( bak )
result = parse ( bak )
except Exception as f :
logger . error (
" (ALIAS) ID ALIAS MAPPER: Tried backup %s file, but couldn ' t load that either: %s " ,
@ -404,57 +407,6 @@ class DefaultAliasLoader(AliasLoader):
pass
return out
def _load_server_tsv_with_backup (
self ,
path : Path ,
file_name : str ,
expected_checksum : str | None ,
) - > dict [ str , str ] :
""" Legacy mk_aliases server_ids load with .bak fallback. """
full = path / file_name
bak = path / f " { file_name } .bak "
result : dict [ str , str ] = { }
loaded_from_primary = False
try :
# Raise (not just skip) on a missing primary file too, so the except block
# below falls back to .bak instead of silently ending up with an empty dict
# (e.g. first boot with the download still failing).
if not full . is_file ( ) :
raise FileNotFoundError ( f " ' { file_name } ' file does not exist " )
if expected_checksum and _blake2bsum ( full ) != expected_checksum :
raise ValueError ( " bad checksum " )
result = self . _load_server_tsv ( path , file_name )
if not result :
raise ValueError ( " empty server_ids " )
loaded_from_primary = True
except Exception as e :
logger . error (
" (ALIAS) ID ALIAS MAPPER: problem loading server_ids file ( %s ), falling back to .bak " ,
e ,
)
if bak . is_file ( ) :
try :
result = self . _load_server_tsv ( path , f " { file_name } .bak " )
except Exception as f :
logger . error (
" (ALIAS) ID ALIAS MAPPER: Tried backup server_ids file, but couldn ' t load that either: %s " ,
f ,
)
if result :
logger . info ( " (ALIAS) ID ALIAS MAPPER: server_ids dictionary is available " )
else :
logger . warning ( " (ALIAS) ID ALIAS MAPPER: server_ids dictionary is empty " )
if loaded_from_primary and full . is_file ( ) :
try :
_atomic_copy ( full , bak )
except OSError as g :
logger . info (
" (ALIAS) ID ALIAS MAPPER: couldn ' t make backup copy of server_ids file %s " ,
g ,
)
return result
def load_subscriber_profiles ( self , config : dict [ str , Any ] ) - > dict [ int , dict [ str , str ] ] :
""" Load { id: { callsign, fname, surname, talker_alias?}} from subscriber JSON files. """
aliases = config . get ( " ALIASES " , { } )