Compare commits
107 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
21c8087281 | |
|
|
6c22819fe3 | |
|
|
2546fd5442 | |
|
|
59de0e5bd3 | |
|
|
1ff51cceef | |
|
|
3a9f23f801 | |
|
|
bca9017897 | |
|
|
6e21c57e2f | |
|
|
87829a5f9f | |
|
|
a897ff7892 | |
|
|
058b40ce09 | |
|
|
e903ad4cc5 | |
|
|
ed5e4d1495 | |
|
|
21abf0b040 | |
|
|
a537f6b1e8 | |
|
|
2356ccf216 | |
|
|
713a147011 | |
|
|
2dfcbd5383 | |
|
|
14cfa39aab | |
|
|
3ff636b8bd | |
|
|
dfd4182620 | |
|
|
27baed8a5e | |
|
|
6296d16081 | |
|
|
d73bdaa762 | |
|
|
24121955d1 | |
|
|
9610bc8687 | |
|
|
88aa091b27 | |
|
|
2963f7875e | |
|
|
42b5e097bb | |
|
|
8f2578581c | |
|
|
73657083d2 | |
|
|
024e7486b6 | |
|
|
5519ea4114 | |
|
|
7fa5c23c2b | |
|
|
ca199e72e0 | |
|
|
803a26de8c | |
|
|
50d9d8689d | |
|
|
cf42b58994 | |
|
|
e1503906d4 | |
|
|
5ffadeb666 | |
|
|
d9f381ce7e | |
|
|
d11ff13df2 | |
|
|
b9fdc9838e | |
|
|
4774f98f4f | |
|
|
89376ded87 | |
|
|
0936f90c49 | |
|
|
2380185963 | |
|
|
4663b5c998 | |
|
|
a7ab68f2a6 | |
|
|
d55c01002b | |
|
|
360b267fcb | |
|
|
55097acf58 | |
|
|
95cfec18ad | |
|
|
8ec0e17475 | |
|
|
cab3cda3c7 | |
|
|
bb6797f17a | |
|
|
81fc0ab66f | |
|
|
b52a8da86b | |
|
|
c5d7cb0bd2 | |
|
|
5d54ecacc1 | |
|
|
17a3a8a8ff | |
|
|
77fd38bbab | |
|
|
5e14fa8fd5 | |
|
|
0008d471d0 | |
|
|
2b0eda07a8 | |
|
|
4d6b4768ff | |
|
|
0446c1a542 | |
|
|
5d61ffd90c | |
|
|
aa14ff9b65 | |
|
|
b34e83060d | |
|
|
787186bc60 | |
|
|
917627c1da | |
|
|
3da95c45a2 | |
|
|
f7804a2515 | |
|
|
763c240488 | |
|
|
1797759d03 | |
|
|
c4ecbc4b86 | |
|
|
830d4b3441 | |
|
|
c60d1a53aa | |
|
|
708599551e | |
|
|
4f7cd970b5 | |
|
|
c086ff60fe | |
|
|
1ff6419804 | |
|
|
6ec6a0d2bf | |
|
|
23786bc690 | |
|
|
4ca991f98b | |
|
|
448c853760 | |
|
|
e4f43da5b5 | |
|
|
92c14de40a | |
|
|
a402d424cb | |
|
|
184cef1e89 | |
|
|
27f31fe079 | |
|
|
4220ab6aea | |
|
|
eb13aef68d | |
|
|
01def8e20d | |
|
|
c037e136ae | |
|
|
58fba9e135 | |
|
|
5e8bf5ae4e | |
|
|
293a8e4642 | |
|
|
90d7d657bd | |
|
|
4b46079ea8 | |
|
|
b4b9d51ae0 | |
|
|
5794bb1cfe | |
|
|
07b93d3e35 | |
|
|
387b5752b8 | |
|
|
47ad817d3b | |
|
|
7fb03aa839 |
|
|
@ -77,9 +77,11 @@ build-release: # This job runs in the build stage, which runs first.
|
|||
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
||||
- docker buildx build --no-cache -f docker-configs/Dockerfile-ci -t gitlab.hacknix.net:5050/hacknix/freedmr:latest -t gitlab.hacknix.net:5050/hacknix/freedmr:$CI_COMMIT_TAG-with-proxy -t gitlab.hacknix.net:5050/hacknix/freedmr:development-latest --platform linux/arm/v7,linux/amd64,linux/i386,linux/arm64 --push .
|
||||
#- docker buildx build --no-cache -f docker-configs/Dockerfile-ci -t hacknix/freedmr:latest -t gitlab.hacknix.net:5050/hacknix/freedmr:latest -t hacknix/$CI_COMMIT_TAG-with-proxy -t gitlab.hacknix.net:5050/hacknix/freedmr:$CI_COMMIT_TAG-with-proxy -t hacknix/freedmr:development-latest -t gitlab.hacknix.net:5050/hacknix/freedmr:development-latest --platform linux/amd64,linux/i386 --push .
|
||||
|
||||
|
||||
|
||||
- echo "Compile complete."
|
||||
release:
|
||||
tag_name: $CI_COMMIT_TAG-with-proxy
|
||||
name: 'Release $CI_COMMIT_TAG-with-proxy'
|
||||
description: 'Release created using CI.'
|
||||
|
||||
only:
|
||||
- tags
|
||||
|
|
|
|||
|
|
@ -0,0 +1,148 @@
|
|||
#
|
||||
###############################################################################
|
||||
# Copyright (C) 2023 Simon Adlem, G7RZU <g7rzu@gb7fr.org.uk>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
###############################################################################
|
||||
|
||||
from spyne import ServiceBase, rpc, Integer, Decimal, UnsignedInteger32, Unicode, Iterable, error
|
||||
from dmr_utils3.utils import bytes_3, bytes_4
|
||||
|
||||
|
||||
class FD_APIUserDefinedContext(object):
|
||||
def __init__(self,CONFIG,BRIDGES):
|
||||
self.CONFIG = CONFIG
|
||||
self.BRIDGES = BRIDGES
|
||||
|
||||
def getconfig(self):
|
||||
return self.CONFIG
|
||||
|
||||
def getbridges(self):
|
||||
return self.BRIDGES
|
||||
|
||||
def validateKey(self,dmrid,key):
|
||||
systems = self.CONFIG['SYSTEMS']
|
||||
dmrid = bytes_4(dmrid)
|
||||
print(dmrid)
|
||||
for system in systems:
|
||||
if systems[system]['MODE'] == 'MASTER':
|
||||
for peerid in systems[system]['PEERS']:
|
||||
print(peerid)
|
||||
if peerid == dmrid:
|
||||
try:
|
||||
if key == systems[system]['_opt_key']:
|
||||
return(system)
|
||||
else:
|
||||
return(False)
|
||||
except KeyError:
|
||||
return(False)
|
||||
return(False)
|
||||
|
||||
def validateSystemKey(self,systemkey):
|
||||
if systemkey == self.CONFIG['GLOBAL']['SYSTEM_API_KEY']:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def reset(self,system):
|
||||
self.CONFIG['SYSTEMS'][system]['_reset'] = True
|
||||
|
||||
def options(self,system,options):
|
||||
self.CONFIG['SYSTEMS'][system]['OPTIONS'] = options
|
||||
|
||||
def getoptions(self,system):
|
||||
return self.CONFIG['SYSTEMS'][system]['OPTIONS']
|
||||
|
||||
def killserver(self):
|
||||
self.CONFIG['GLOBAL']['_KILL_SERVER'] = True
|
||||
|
||||
def resetAllConnections(self):
|
||||
systems = self.CONFIG['SYSTEMS']
|
||||
for system in systems:
|
||||
self.CONFIG['SYSTEMS'][system]['_reset'] = True
|
||||
|
||||
|
||||
|
||||
class FD_API(ServiceBase):
|
||||
_version = 0.1
|
||||
|
||||
#return API version
|
||||
@rpc(Unicode, _returns=Decimal())
|
||||
def version(ctx, sessionid):
|
||||
return(FD_API._version)
|
||||
|
||||
@rpc()
|
||||
def dummy(ctx):
|
||||
pass
|
||||
|
||||
######################
|
||||
#User level API calls#
|
||||
######################
|
||||
@rpc(UnsignedInteger32,Unicode)
|
||||
def reset(ctx,dmrid,key):
|
||||
system = ctx.udc.validateKey(int(dmrid),key)
|
||||
if system:
|
||||
ctx.udc.reset(system)
|
||||
else:
|
||||
raise error.InvalidCredentialsError()
|
||||
|
||||
@rpc(UnsignedInteger32,Unicode,Unicode)
|
||||
def setoptions(ctx,dmrid,key,options):
|
||||
system = ctx.udc.validateKey(int(dmrid),key)
|
||||
if system:
|
||||
ctx.udc.options(system,options)
|
||||
else:
|
||||
raise error.InvalidCredentialsError()
|
||||
|
||||
@rpc(UnsignedInteger32,Unicode,_returns=Unicode())
|
||||
def getoptions(ctx,dmrid,key):
|
||||
system = ctx.udc.validateKey(int(dmrid),key)
|
||||
if system:
|
||||
return ctx.udc.getoptions(system)
|
||||
else:
|
||||
raise error.InvalidCredentialsError()
|
||||
|
||||
########################
|
||||
#System level API calls#
|
||||
########################
|
||||
@rpc(Unicode)
|
||||
def killserver(ctx,systemkey):
|
||||
if ctx.udc.validateSystemKey(systemkey):
|
||||
return ctx.udc.killserver()
|
||||
else:
|
||||
raise error.InvalidCredentialsError()
|
||||
|
||||
@rpc(Unicode)
|
||||
def resetall(ctx,systemkey):
|
||||
if ctx.udc.validateSystemKey(systemkey):
|
||||
return ctx.udc.resetAllConnections()
|
||||
else:
|
||||
raise error.InvalidCredentialsError()
|
||||
|
||||
@rpc(Unicode,_returns=Unicode())
|
||||
def getconfig(ctx,systemkey):
|
||||
if ctx.udc.validateSystemKey(systemkey):
|
||||
return ctx.udc.getconfig()
|
||||
else:
|
||||
raise error.InvalidCredentialsError()
|
||||
|
||||
@rpc(Unicode,_returns=Unicode())
|
||||
def getbridges(ctx,systemkey):
|
||||
if ctx.udc.validateSystemKey(systemkey):
|
||||
return ctx.udc.getbridges()
|
||||
else:
|
||||
raise error.InvalidCredentialsError()
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,6 @@
|
|||
°Î†Tã²?à×¢Bp¥v5ã<0F>]#t¥ÝÑ÷Áb9òý;©ç@
|
||||
!ßAÛ@¡dLÕÜíúb biÇéϺÄaI#5îw™Æg !Ï!¼]£%
¼]Œ~ %.½+œ|¡a:-ž~i†×®6>\‡®îwm#$+¢}p$\:gïp¡fQɰ*b¾XT§™÷-^ª]#•d—%¢fºGÂ
|
||||
ÎXŒûDat#ÂI"yCt¡ÈÇ=•uq¦=…6bPæy\\à"U“òáæ˜åGPVyˆ+oHŽ«ü©ÜE+Hy¢®?‘^zlRæôlH—nB¶X#›X”nR„]gëâs;¸»Û±]ú "ÕR<C395>‚ö\Î(í<>g_«
|
||||
=XóŸãâ"L hÉ'K<>p
|
||||
ä]¡yb´’B¢<0C>)åHŒ
|
||||
§I™‹g8ˆÉ ìÊvЉdš+ï%)«äR"¦}wr‚'£e7(}ýY
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -30,11 +30,11 @@ SUB_ACL: DENY:1
|
|||
TGID_TS1_ACL: PERMIT:ALL
|
||||
TGID_TS2_ACL: PERMIT:ALL
|
||||
DEFAULT_UA_TIMER: 60
|
||||
SINGLE_MODE: True
|
||||
VOICE_IDENT: True
|
||||
TS1_STATIC:
|
||||
TS2_STATIC:
|
||||
DEFAULT_REFLECTOR: 0
|
||||
SINGLE_MODE: True
|
||||
VOICE_IDENT: True
|
||||
ANNOUNCEMENT_LANGUAGE: en_GB
|
||||
GENERATOR: 100
|
||||
ALLOW_UNREG_ID: False
|
||||
|
|
|
|||
|
|
@ -201,6 +201,7 @@ TGID_TS2_ACL: PERMIT:ALL
|
|||
DEFAULT_UA_TIMER: 10
|
||||
SINGLE_MODE: True
|
||||
VOICE_IDENT: True
|
||||
#the next three lines no longer have any effect
|
||||
TS1_STATIC:
|
||||
TS2_STATIC:
|
||||
DEFAULT_REFLECTOR: 0
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
|
||||
from twisted.internet import reactor
|
||||
from twisted.web.xmlrpc import Proxy
|
||||
|
||||
|
||||
def printValue(value):
|
||||
print(repr(value))
|
||||
reactor.stop()
|
||||
|
||||
|
||||
def printError(error):
|
||||
print("error", error)
|
||||
reactor.stop()
|
||||
|
||||
|
||||
def capitalize(value):
|
||||
print(value)
|
||||
|
||||
|
||||
proxy = Proxy(b"http://localhost:7080/xmlrpc")
|
||||
# The callRemote method accepts a method name and an argument list.
|
||||
proxy.callRemote("FD_API.reset", '2', '55555').addCallbacks(capitalize, printError)
|
||||
reactor.run()
|
||||
444
bridge_master.py
444
bridge_master.py
|
|
@ -40,6 +40,8 @@ import re
|
|||
import copy
|
||||
from setproctitle import setproctitle
|
||||
from collections import deque
|
||||
from random import randint
|
||||
import secrets
|
||||
|
||||
#from crccheck.crc import Crc32
|
||||
from hashlib import blake2b
|
||||
|
|
@ -48,6 +50,15 @@ from hashlib import blake2b
|
|||
from twisted.internet.protocol import Factory, Protocol
|
||||
from twisted.protocols.basic import NetstringReceiver
|
||||
from twisted.internet import reactor, task
|
||||
from twisted.web.server import Site
|
||||
|
||||
from spyne import Application
|
||||
from spyne.server.twisted import TwistedWebResource
|
||||
from spyne.protocol.http import HttpRpc
|
||||
from spyne.protocol.json import JsonDocument
|
||||
|
||||
|
||||
|
||||
|
||||
# Things we import from the main hblink module
|
||||
from hblink import HBSYSTEM, OPENBRIDGE, systems, hblink_handler, reportFactory, REPORT_OPCODES, mk_aliases, acl_check
|
||||
|
|
@ -58,6 +69,7 @@ from config import acl_build
|
|||
import log
|
||||
from const import *
|
||||
from mk_voice import pkt_gen
|
||||
from utils import load_json, save_json
|
||||
#from voice_lib import words
|
||||
|
||||
#Read voices
|
||||
|
|
@ -75,10 +87,13 @@ logger = logging.getLogger(__name__)
|
|||
#REGEX
|
||||
import re
|
||||
|
||||
#pretty print
|
||||
import pprint
|
||||
|
||||
from binascii import b2a_hex as ahex
|
||||
|
||||
from AMI import AMI
|
||||
|
||||
from API import FD_API, FD_APIUserDefinedContext
|
||||
|
||||
# Does anybody read this stuff? There's a PEP somewhere that says I should do this.
|
||||
__author__ = 'Cortney T. Buffington, N0MJS, Forked by Simon Adlem - G7RZU'
|
||||
|
|
@ -132,6 +147,27 @@ def config_reports(_config, _factory):
|
|||
|
||||
return report_server
|
||||
|
||||
# Start API server
|
||||
def config_API(_config, _bridges):
|
||||
|
||||
|
||||
application = Application([FD_API],
|
||||
tns='freedmr.api',
|
||||
in_protocol=HttpRpc(validator='soft'),
|
||||
out_protocol=JsonDocument()
|
||||
)
|
||||
|
||||
def _on_method_call(ctx):
|
||||
ctx.udc = FD_APIUserDefinedContext(CONFIG,_bridges)
|
||||
|
||||
application.event_manager.add_listener('method_call', _on_method_call)
|
||||
|
||||
resource = TwistedWebResource(application)
|
||||
site = Site(resource)
|
||||
|
||||
r = reactor.listenTCP(8000, site, interface='0.0.0.0')
|
||||
return(r)
|
||||
|
||||
|
||||
# Import Bridging rules
|
||||
# Note: A stanza *must* exist for any MASTER or CLIENT configured in the main
|
||||
|
|
@ -229,7 +265,7 @@ def make_default_reflector(reflector,_tmout,system):
|
|||
if bridge not in BRIDGES:
|
||||
BRIDGES[bridge] = []
|
||||
make_single_reflector(bytes_3(reflector),_tmout, system)
|
||||
bridgetemp = deque()
|
||||
bridgetemp = []
|
||||
for bridgesystem in BRIDGES[bridge]:
|
||||
if bridgesystem['SYSTEM'] == system and bridgesystem['TS'] == 2:
|
||||
bridgetemp.append({'SYSTEM': system, 'TS': 2, 'TGID': bytes_3(9),'ACTIVE': True,'TIMEOUT': _tmout * 60,'TO_TYPE': 'OFF','OFF': [],'ON': [bytes_3(reflector),],'RESET': [], 'TIMER': time() + (_tmout * 60)})
|
||||
|
|
@ -242,7 +278,7 @@ def make_static_tg(tg,ts,_tmout,system):
|
|||
#_tmout = CONFIG['SYSTEMS'][system]['DEFAULT_UA_TIMER']
|
||||
if str(tg) not in BRIDGES:
|
||||
make_single_bridge(bytes_3(tg),system,ts,_tmout)
|
||||
bridgetemp = deque()
|
||||
bridgetemp = []
|
||||
for bridgesystem in BRIDGES[str(tg)]:
|
||||
if bridgesystem['SYSTEM'] == system and bridgesystem['TS'] == ts:
|
||||
bridgetemp.append({'SYSTEM': system, 'TS': ts, 'TGID': bytes_3(tg),'ACTIVE': True,'TIMEOUT': _tmout * 60,'TO_TYPE': 'OFF','OFF': [],'ON': [bytes_3(tg),],'RESET': [], 'TIMER': time() + (_tmout * 60)})
|
||||
|
|
@ -253,7 +289,7 @@ def make_static_tg(tg,ts,_tmout,system):
|
|||
|
||||
def reset_static_tg(tg,ts,_tmout,system):
|
||||
#_tmout = CONFIG['SYSTEMS'][system]['DEFAULT_UA_TIMER']
|
||||
bridgetemp = deque()
|
||||
bridgetemp = []
|
||||
try:
|
||||
for bridgesystem in BRIDGES[str(tg)]:
|
||||
if bridgesystem['SYSTEM'] == system and bridgesystem['TS'] == ts:
|
||||
|
|
@ -266,20 +302,50 @@ def reset_static_tg(tg,ts,_tmout,system):
|
|||
logger.exception('(%s) KeyError in reset_static_tg() - bridge gone away? TG: %s',system,tg)
|
||||
return
|
||||
|
||||
def reset_default_reflector(reflector,_tmout,system):
|
||||
bridge = ''.join(['#',str(reflector)])
|
||||
#_tmout = CONFIG['SYSTEMS'][system]['DEFAULT_UA_TIMER']
|
||||
if bridge not in BRIDGES:
|
||||
BRIDGES[bridge] = []
|
||||
make_single_reflector(bytes_3(reflector),_tmout, system)
|
||||
bridgetemp = deque()
|
||||
for bridgesystem in BRIDGES[bridge]:
|
||||
if bridgesystem['SYSTEM'] == system and bridgesystem['TS'] == 2:
|
||||
bridgetemp.append({'SYSTEM': system, 'TS': 2, 'TGID': bytes_3(9),'ACTIVE': False,'TIMEOUT': _tmout * 60,'TO_TYPE': 'ON','OFF': [],'ON': [bytes_3(reflector),],'RESET': [], 'TIMER': time() + (_tmout * 60)})
|
||||
else:
|
||||
bridgetemp.append(bridgesystem)
|
||||
BRIDGES[bridge] = bridgetemp
|
||||
|
||||
#def reset_default_reflector(reflector,_tmout,system):
|
||||
#print(reflector)
|
||||
#bridge = ''.join(['#',str(reflector)])
|
||||
#print(bridge)
|
||||
##_tmout = CONFIG['SYSTEMS'][system]['DEFAULT_UA_TIMER']
|
||||
#if bridge not in BRIDGES:
|
||||
#BRIDGES[bridge] = []
|
||||
#make_single_reflector(bytes_3(reflector),_tmout, system)
|
||||
#bridgetemp = deque()
|
||||
#for bridgesystem in BRIDGES[bridge]:
|
||||
#print(bridgesystem)
|
||||
#if bridgesystem['SYSTEM'] == system and bridgesystem['TS'] == 2:
|
||||
#print(bridgesystem)
|
||||
#bridgetemp.append({'SYSTEM': system, 'TS': 2, 'TGID': bytes_3(9),'ACTIVE': False,'TIMEOUT': _tmout * 60,'TO_TYPE': 'ON','OFF': [],'ON': [bytes_3(reflector),],'RESET': [], 'TIMER': time() + (_tmout * 60)})
|
||||
#else:
|
||||
#bridgetemp.append(bridgesystem)
|
||||
#print(bridgetemp)
|
||||
#BRIDGES[bridge] = bridgetemp
|
||||
#print(BRIDGES[bridge])
|
||||
|
||||
def reset_all_reflector_system(_tmout,resetSystem):
|
||||
logger.trace('RST: In reset_all_reflector_system - timeout: %s, resetSystem: %s',_tmout,resetSystem)
|
||||
bt = {}
|
||||
for system in CONFIG['SYSTEMS']:
|
||||
logger.trace('RST: for %s in SYSTEMS',system)
|
||||
for bridge in BRIDGES:
|
||||
logger.trace('RST: for %s in BRIDGES',bridge)
|
||||
if bridge[0:1] == '#':
|
||||
bridgetemp = []
|
||||
for bridgesystem in BRIDGES[bridge]:
|
||||
logger.trace('RST: for %s in BRIDGES[%s]',bridgesystem,bridge)
|
||||
if bridgesystem['SYSTEM'] == resetSystem and bridgesystem['TS'] == 2:
|
||||
logger.trace('RST: MATCH: setting inactive for %s',bridgesystem['SYSTEM'])
|
||||
bridgetemp.append({'SYSTEM': resetSystem, 'TS': 2, 'TGID': bytes_3(9),'ACTIVE': False,'TIMEOUT': _tmout * 60,'TO_TYPE': 'ON','OFF': [],'ON': [bytes_3(int(bridge[1:])),],'RESET': [], 'TIMER': time() + (_tmout * 60)})
|
||||
else:
|
||||
logger.trace('RST: NO MATCH: using existing: %s',bridgesystem)
|
||||
bridgetemp.append(bridgesystem)
|
||||
logger.trace('RST: bridgetemp %s',bridgetemp)
|
||||
#BRIDGES[bridge] = bridgetemp
|
||||
bt[bridge] = bridgetemp
|
||||
for bridge in bt:
|
||||
BRIDGES[bridge] = bt[bridge]
|
||||
|
||||
|
||||
def make_single_reflector(_tgid,_tmout,_sourcesystem):
|
||||
_tgid_s = str(int_id(_tgid))
|
||||
_bridge = ''.join(['#',_tgid_s])
|
||||
|
|
@ -298,22 +364,53 @@ def make_single_reflector(_tgid,_tmout,_sourcesystem):
|
|||
if _system[0:3] == 'OBP' and (int_id(_tgid) >= 79 and (int_id(_tgid) < 9990 or int_id(_tgid) > 9999)):
|
||||
BRIDGES[_bridge].append({'SYSTEM': _system, 'TS': 1, 'TGID': _tgid,'ACTIVE': True,'TIMEOUT': '','TO_TYPE': 'NONE','OFF': [],'ON': [],'RESET': [], 'TIMER': time()})
|
||||
|
||||
def remove_bridge_system(system):
|
||||
#def remove_bridge_system(system):
|
||||
#_bridgestemp = {}
|
||||
#_bridgetemp = {}
|
||||
#for _bridge in BRIDGES:
|
||||
#for _bridgesystem in BRIDGES[_bridge]:
|
||||
#if _bridgesystem['SYSTEM'] != system:
|
||||
#if _bridge not in _bridgestemp:
|
||||
#_bridgestemp[_bridge] = []
|
||||
#_bridgestemp[_bridge].append(_bridgesystem)
|
||||
|
||||
#else:
|
||||
#if _bridge not in _bridgestemp:
|
||||
#_bridgestemp[_bridge] = []
|
||||
#_bridgestemp[_bridge].append({'SYSTEM': system, 'TS': _bridgesystem['TS'], 'TGID': _bridgesystem['TGID'],'ACTIVE': False,'TIMEOUT': _bridgesystem['TIMEOUT'],'TO_TYPE': 'ON','OFF': [],'ON': [_bridgesystem['TGID'],],'RESET': [], 'TIMER': time() + _bridgesystem['TIMEOUT']})
|
||||
|
||||
def remove_bridge_system(remsystem):
|
||||
bt = {}
|
||||
for system in CONFIG['SYSTEMS']:
|
||||
for bridge in BRIDGES:
|
||||
bridgetemp = []
|
||||
for bridgesystem in BRIDGES[bridge]:
|
||||
if bridgesystem['SYSTEM'] == remsystem:
|
||||
bridgetemp.append({'SYSTEM': system, 'TS': bridgesystem['TS'], 'TGID': bridgesystem['TGID'],'ACTIVE': False,'TIMEOUT': bridgesystem['TIMEOUT'],'TO_TYPE': 'ON','OFF': [],'ON': [bridgesystem['TGID'],],'RESET': [], 'TIMER': time() + bridgesystem['TIMEOUT']})
|
||||
logger.debug('RBS False: %s: %s',system, {'SYSTEM': system, 'TS': bridgesystem['TS'], 'TGID': bridgesystem['TGID'],'ACTIVE': False,'TIMEOUT': bridgesystem['TIMEOUT'],'TO_TYPE': 'ON','OFF': [],'ON': [bridgesystem['TGID'],],'RESET': [], 'TIMER': time() + bridgesystem['TIMEOUT']} )
|
||||
else:
|
||||
bridgetemp.append(bridgesystem)
|
||||
logger.debug('RBS: existing %s',bridgesystem)
|
||||
bt[bridge] = bridgetemp
|
||||
for bridge in bt:
|
||||
BRIDGES[bridge] = bt[bridge]
|
||||
|
||||
def update_timeout(system,_tmout):
|
||||
_bridgestemp = {}
|
||||
_bridgetemp = {}
|
||||
for _bridge in BRIDGES:
|
||||
for _bridgesystem in BRIDGES[_bridge]:
|
||||
if _bridgesystem['SYSTEM'] != system:
|
||||
if _bridge not in _bridgestemp:
|
||||
_bridgestemp[_bridge] = []
|
||||
_bridgestemp[_bridge].append(_bridgesystem)
|
||||
continue
|
||||
else:
|
||||
if _bridge not in _bridgestemp:
|
||||
_bridgestemp[_bridge] = []
|
||||
_bridgestemp[_bridge].append({'SYSTEM': system, 'TS': _bridgesystem['TS'], 'TGID': _bridgesystem['TGID'],'ACTIVE': False,'TIMEOUT': _bridgesystem['TIMEOUT'],'TO_TYPE': 'ON','OFF': [],'ON': [_bridgesystem['TGID'],],'RESET': [], 'TIMER': time() + _bridgesystem['TIMEOUT']})
|
||||
|
||||
_bridgesystem['TIMEOUT'] = _tmout * 60
|
||||
_bridgestemp[_bridge].append(_bridgesystem)
|
||||
|
||||
|
||||
BRIDGES.update(_bridgestemp)
|
||||
|
||||
|
||||
|
||||
# Run this every minute for rule timer updates
|
||||
def rule_timer_loop():
|
||||
|
|
@ -359,7 +456,9 @@ def rule_timer_loop():
|
|||
|
||||
if _bridge_used == False:
|
||||
_remove_bridges.append(_bridge)
|
||||
|
||||
|
||||
pretty = pprint.pformat(BRIDGES)
|
||||
logger.debug('(ROUTER) BRIDGES: %s',pretty)
|
||||
for _bridgerem in _remove_bridges:
|
||||
del BRIDGES[_bridgerem]
|
||||
logger.debug('(ROUTER) Unused conference bridge %s removed',_bridgerem)
|
||||
|
|
@ -378,7 +477,7 @@ def statTrimmer():
|
|||
_bridge_stat = True
|
||||
if _system['TO_TYPE'] == 'ON' and _system['ACTIVE']:
|
||||
_in_use = True
|
||||
elif _system['TO_TYPE'] == 'OFF' and not _system['ACTIVE']:
|
||||
elif _system['TO_TYPE'] == 'OFF':
|
||||
_in_use = True
|
||||
if _bridge_stat and not _in_use:
|
||||
_remove_bridges.append(_bridge)
|
||||
|
|
@ -390,8 +489,15 @@ def statTrimmer():
|
|||
|
||||
#Debug and fix bridge table issues.
|
||||
def bridgeDebug():
|
||||
logger.info('(BRIDGEDEBUG) Running bridge debug')
|
||||
logger.debug('(BRIDGEDEBUG) Running bridge debug')
|
||||
_rst_time = time()
|
||||
statroll = 0
|
||||
|
||||
#Kill off any bridges that should not exist, ever.
|
||||
for b in ['0','1','2','3','4','5','6','7','8','9']:
|
||||
BRIDGES.pop(b,None)
|
||||
BRIDGES.pop('#'+b, None)
|
||||
|
||||
for system in CONFIG['SYSTEMS']:
|
||||
bridgeroll = 0
|
||||
dialroll = 0
|
||||
|
|
@ -420,17 +526,24 @@ def bridgeDebug():
|
|||
if enabled_system['ACTIVE'] and _bridge and _bridge[0:1] == '#':
|
||||
times[enabled_system['TIMER']] = _bridge
|
||||
ordered = sorted(times.keys())
|
||||
_setbridge = times[ordered.pop()]
|
||||
#bridgetmout = ordered.pop()
|
||||
#_setbridge = str(times[bridgetmout])
|
||||
if CONFIG['SYSTEMS'][system]['MODE'] == 'MASTER':
|
||||
logger.warning('(BRIDGEDEBUG) setting %s dial bridge to %s as this bridge has the longest timer set to run',system, _setbridge)
|
||||
for _bridge in set(times.values()):
|
||||
logger.warning('(BRIDGEDEBUG) deactivating system: %s for bridge: %s',system,_bridge)
|
||||
bridgetemp = []
|
||||
for bridgesystem in BRIDGES[_bridge]:
|
||||
if _bridge[0:1] == '#':
|
||||
_setbridge = int(_bridge[1:])
|
||||
else:
|
||||
_setbridge = int(_bridge)
|
||||
|
||||
for _tstamp in ordered:
|
||||
for _bridge in times.values():
|
||||
for _entry in BRIDGES[_bridge]:
|
||||
if bridgesystem['SYSTEM'] == system and bridgesystem['TS'] == 2:
|
||||
bridgetemp.append({'SYSTEM': system, 'TS': 2, 'TGID': bytes_3(9),'ACTIVE': False,'TIMEOUT': _tmout * 60,'TO_TYPE': 'ON','OFF': [],'ON': [bytes_3(_setbridge),],'RESET': [], 'TIMER': _rst_time + (_tmout * 60)})
|
||||
else:
|
||||
bridgetemp.append(bridgesystem)
|
||||
BRIDGES[_bridge] = bridgetemp
|
||||
|
||||
if CONFIG['SYSTEMS'][system]['MODE'] == 'MASTER':
|
||||
if _entry['SYSTEM'] == system:
|
||||
_entry['ACTIVE'] = False
|
||||
|
||||
logger.info('(BRIDGEDEBUG) The server currently has %s STATic bridges',statroll)
|
||||
|
||||
|
|
@ -781,21 +894,29 @@ def bridge_reset():
|
|||
logger.debug('(BRIDGERESET) Running bridge resetter')
|
||||
for _system in CONFIG['SYSTEMS']:
|
||||
if '_reset' in CONFIG['SYSTEMS'][_system] and CONFIG['SYSTEMS'][_system]['_reset']:
|
||||
logger.info('(BRIDGERESET) Bridge reset for %s - no peers',_system)
|
||||
logger.info('(BRIDGERESET) Bridge reset for %s - no peers or API reset called',_system)
|
||||
remove_bridge_system(_system)
|
||||
try:
|
||||
del(CONFIG['SYSTEMS'][_system]['_opt_key'])
|
||||
except:
|
||||
pass
|
||||
CONFIG['SYSTEMS'][_system]['_reset'] = False
|
||||
continue
|
||||
|
||||
CONFIG['SYSTEMS'][_system]['_resetlog'] = False
|
||||
if 'OPTIONS' in CONFIG['SYSTEMS'][_system]['OPTIONS']:
|
||||
CONFIG['SYSTEMS'][_system]['_reloadoptions'] = True
|
||||
|
||||
def options_config():
|
||||
logger.debug('(OPTIONS) Running options parser')
|
||||
|
||||
prohibitedTGs = [0,1,2,3,4,5,9,9990,9991,9992,9993,9994,9995,9996,9997,9998,9999]
|
||||
prohibitedTGs = [0,1,2,3,4,5,6,7,8,9,9990,9991,9992,9993,9994,9995,9996,9997,9998,9999]
|
||||
|
||||
for _system in CONFIG['SYSTEMS']:
|
||||
systemList = CONFIG['SYSTEMS'].keys()
|
||||
for _system in systemList:
|
||||
try:
|
||||
if CONFIG['SYSTEMS'][_system]['MODE'] != 'MASTER':
|
||||
continue
|
||||
if '_reset' in CONFIG['SYSTEMS'][_system] and CONFIG['SYSTEMS'][_system]['_reset']:
|
||||
continue
|
||||
if CONFIG['SYSTEMS'][_system]['ENABLED'] == True:
|
||||
if 'OPTIONS' in CONFIG['SYSTEMS'][_system]:
|
||||
_options = {}
|
||||
|
|
@ -811,6 +932,21 @@ def options_config():
|
|||
continue
|
||||
_options[k] = v
|
||||
logger.debug('(OPTIONS) Options found for %s',_system)
|
||||
|
||||
if '_opt_key' in CONFIG['SYSTEMS'][_system] and CONFIG['SYSTEMS'][_system]['_opt_key']:
|
||||
if 'KEY' not in _options:
|
||||
logger.debug('(OPTIONS) %s, options key set but no key in options string, skipping',_system)
|
||||
continue
|
||||
elif CONFIG['SYSTEMS'][_system]['_opt_key'] != _options['KEY']:
|
||||
logger.debug('(OPTIONS) %s, options key set but key sent does not match, skipping',_system)
|
||||
continue
|
||||
elif 'KEY' in _options and _options['KEY']:
|
||||
logger.debug('(OPTIONS) %s, _opt_key not set but key sent. Setting to sent key',_system)
|
||||
CONFIG['SYSTEMS'][_system]['_opt_key'] = _options['KEY']
|
||||
else:
|
||||
logger.debug('(OPTIONS) %s, _opt_key not set and no key sent. Set to false',_system)
|
||||
CONFIG['SYSTEMS'][_system]['_opt_key'] = False
|
||||
|
||||
|
||||
if 'DIAL' in _options:
|
||||
_options['DEFAULT_REFLECTOR'] = _options.pop('DIAL')
|
||||
|
|
@ -946,40 +1082,24 @@ def options_config():
|
|||
|
||||
_tmout = int(_options['DEFAULT_UA_TIMER'])
|
||||
|
||||
if int(_options['DEFAULT_UA_TIMER']) != CONFIG['SYSTEMS'][_system]['DEFAULT_UA_TIMER']:
|
||||
if ('_reloadoptions' in CONFIG['SYSTEMS'][_system] and CONFIG['SYSTEMS'][_system]['_reloadoptions']) or (int(_options['DEFAULT_UA_TIMER']) != CONFIG['SYSTEMS'][_system]['DEFAULT_UA_TIMER']):
|
||||
logger.debug('(OPTIONS) %s Updating DEFAULT_UA_TIMER for existing bridges.',_system)
|
||||
remove_bridge_system(_system)
|
||||
for _bridge in BRIDGES:
|
||||
ts1 = False
|
||||
ts2 = False
|
||||
for i,e in enumerate(BRIDGES[_bridge]):
|
||||
if e['SYSTEM'] == _system and e['TS'] == 1:
|
||||
ts1 = True
|
||||
if e['SYSTEM'] == _system and e['TS'] == 2:
|
||||
ts2 = True
|
||||
if _bridge[0:1] != '#':
|
||||
if ts1 == False:
|
||||
BRIDGES[_bridge].append({'SYSTEM': _system, 'TS': 1, 'TGID': bytes_3(int(_bridge)),'ACTIVE': False,'TIMEOUT': _tmout * 60,'TO_TYPE': 'ON','OFF': [],'ON': [bytes_3(int(_bridge)),],'RESET': [], 'TIMER': time()})
|
||||
if ts2 == False:
|
||||
BRIDGES[_bridge].append({'SYSTEM': _system, 'TS': 2, 'TGID': bytes_3(int(_bridge)),'ACTIVE': False,'TIMEOUT': _tmout * 60,'TO_TYPE': 'ON','OFF': [],'ON': [bytes_3(int(_bridge)),],'RESET': [], 'TIMER': time()})
|
||||
else:
|
||||
if ts2 == False:
|
||||
BRIDGES[_bridge].append({'SYSTEM': _system, 'TS': 2, 'TGID': bytes_3(9),'ACTIVE': False,'TIMEOUT': _tmout * 60,'TO_TYPE': 'ON','OFF': [bytes_3(4000)],'ON': [],'RESET': [], 'TIMER': time()})
|
||||
update_timeout(_system,_tmout)
|
||||
|
||||
if int(_options['DEFAULT_REFLECTOR']) != CONFIG['SYSTEMS'][_system]['DEFAULT_REFLECTOR']:
|
||||
if int(_options['DEFAULT_REFLECTOR']) > 0:
|
||||
logger.debug('(OPTIONS) %s default reflector changed, updating',_system)
|
||||
reset_default_reflector(CONFIG['SYSTEMS'][_system]['DEFAULT_REFLECTOR'],_tmout,_system)
|
||||
make_default_reflector(int(_options['DEFAULT_REFLECTOR']),_tmout,_system)
|
||||
elif int(_options['DEFAULT_REFLECTOR']) in prohibitedTGs:
|
||||
logger.debug('(OPTIONS) %s default reflector is prohibited, ignoring change',_system)
|
||||
|
||||
if int(_options['DEFAULT_REFLECTOR']) in prohibitedTGs and int(_options['DEFAULT_REFLECTOR']) > 0:
|
||||
logger.debug('(OPTIONS) %s default dial-a-tg is in prohibited list, ignoring change',_system)
|
||||
elif int(_options['DEFAULT_REFLECTOR']) > 0:
|
||||
logger.debug('(OPTIONS) %s default dial-a-tg changed, updating',_system)
|
||||
reset_all_reflector_system(_tmout,_system)
|
||||
make_default_reflector(int(_options['DEFAULT_REFLECTOR']),_tmout,_system)
|
||||
else:
|
||||
logger.debug('(OPTIONS) %s default reflector disabled, updating',_system)
|
||||
reset_default_reflector(int(_options['DEFAULT_REFLECTOR']),_tmout,_system)
|
||||
logger.debug('(OPTIONS) %s default dial-a-tg disabled, updating',_system)
|
||||
reset_all_reflector_system(_tmout,_system)
|
||||
|
||||
ts1 = []
|
||||
if _options['TS1_STATIC'] != CONFIG['SYSTEMS'][_system]['TS1_STATIC']:
|
||||
if ('_reloadoptions' in CONFIG['SYSTEMS'][_system] and CONFIG['SYSTEMS'][_system]['_reloadoptions']) or (_options['TS1_STATIC'] != CONFIG['SYSTEMS'][_system]['TS1_STATIC']):
|
||||
_tmout = int(_options['DEFAULT_UA_TIMER'])
|
||||
logger.debug('(OPTIONS) %s TS1 static TGs changed, updating',_system)
|
||||
ts1 = []
|
||||
|
|
@ -995,10 +1115,12 @@ def options_config():
|
|||
for tg in ts1:
|
||||
if not tg:
|
||||
continue
|
||||
elif int(tg) in prohibitedTGs:
|
||||
logger.debug('(OPTIONS) %s TS1 TG %s is prohibited, ignoring change',_system,tg)
|
||||
tg = int(tg)
|
||||
make_static_tg(tg,1,_tmout,_system)
|
||||
ts2 = []
|
||||
if _options['TS2_STATIC'] != CONFIG['SYSTEMS'][_system]['TS2_STATIC']:
|
||||
if ('_reloadoptions' in CONFIG['SYSTEMS'][_system] and CONFIG['SYSTEMS'][_system]['_reloadoptions']) or (_options['TS2_STATIC'] != CONFIG['SYSTEMS'][_system]['TS2_STATIC']):
|
||||
_tmout = int(_options['DEFAULT_UA_TIMER'])
|
||||
logger.debug('(OPTIONS) %s TS2 static TGs changed, updating',_system)
|
||||
if CONFIG['SYSTEMS'][_system]['TS2_STATIC']:
|
||||
|
|
@ -1006,8 +1128,6 @@ def options_config():
|
|||
for tg in ts2:
|
||||
if not tg or int(tg) == 0 or int(tg) >= 16777215:
|
||||
continue
|
||||
if int(tg) in prohibitedTGs:
|
||||
continue
|
||||
tg = int(tg)
|
||||
reset_static_tg(tg,2,_tmout,_system)
|
||||
ts2 = []
|
||||
|
|
@ -1016,8 +1136,10 @@ def options_config():
|
|||
for tg in ts2:
|
||||
if not tg or int(tg) == 0 or int(tg) >= 16777215:
|
||||
continue
|
||||
if int(tg) in prohibitedTGs:
|
||||
elif int(tg) in prohibitedTGs:
|
||||
logger.debug('(OPTIONS) %s TS2 TG %s is prohibited, ignoring change',_system,tg)
|
||||
continue
|
||||
|
||||
tg = int(tg)
|
||||
make_static_tg(tg,2,_tmout,_system)
|
||||
|
||||
|
|
@ -1025,6 +1147,9 @@ def options_config():
|
|||
CONFIG['SYSTEMS'][_system]['TS2_STATIC'] = _options['TS2_STATIC']
|
||||
CONFIG['SYSTEMS'][_system]['DEFAULT_REFLECTOR'] = int(_options['DEFAULT_REFLECTOR'])
|
||||
CONFIG['SYSTEMS'][_system]['DEFAULT_UA_TIMER'] = int(_options['DEFAULT_UA_TIMER'])
|
||||
|
||||
if '_reloadoptions' in CONFIG['SYSTEMS'][_system] and CONFIG['SYSTEMS'][_system]['_reloadoptions']:
|
||||
CONFIG['SYSTEMS'][_system]['_reloadoptions'] = False
|
||||
except Exception as e:
|
||||
logger.exception('(OPTIONS) caught exception: %s',e)
|
||||
continue
|
||||
|
|
@ -1127,13 +1252,20 @@ class routerOBP(OPENBRIDGE):
|
|||
dmrbits = _target_status[_stream_id]['H_LC'][0:98] + dmrbits[98:166] + _target_status[_stream_id]['H_LC'][98:197]
|
||||
# Create a voice terminator packet (FULL LC)
|
||||
elif _frame_type == HBPF_DATA_SYNC and _dtype_vseq == HBPF_SLT_VTERM:
|
||||
dmrbits = _target_status[_stream_id]['T_LC'][0:98] + dmrbits[98:166] + _target_status[_stream_id]['T_LC'][98:197]
|
||||
try:
|
||||
dmrbits = _target_status[_stream_id]['T_LC'][0:98] + dmrbits[98:166] + _target_status[_stream_id]['T_LC'][98:197]
|
||||
except KeyError:
|
||||
logger.warning('(%s) KeyError - T_LC, Skipping',system)
|
||||
if CONFIG['REPORTS']['REPORT']:
|
||||
call_duration = pkt_time - _target_status[_stream_id]['START']
|
||||
systems[_target['SYSTEM']]._report.send_bridgeEvent('GROUP VOICE,END,TX,{},{},{},{},{},{},{:.2f}'.format(_target['SYSTEM'], int_id(_stream_id), int_id(_peer_id), int_id(_rf_src), _target['TS'], int_id(_target['TGID']), call_duration).encode(encoding='utf-8', errors='ignore'))
|
||||
# Create a Burst B-E packet (Embedded LC)
|
||||
elif _dtype_vseq in [1,2,3,4]:
|
||||
dmrbits = dmrbits[0:116] + _target_status[_stream_id]['EMB_LC'][_dtype_vseq] + dmrbits[148:264]
|
||||
try:
|
||||
dmrbits = dmrbits[0:116] + _target_status[_stream_id]['EMB_LC'][_dtype_vseq] + dmrbits[148:264]
|
||||
except KeyError:
|
||||
logger.warning('(%s) KeyError - EMB_LC, skipping',system)
|
||||
continue
|
||||
dmrpkt = dmrbits.tobytes()
|
||||
_tmp_data = b''.join([_tmp_data, dmrpkt])
|
||||
|
||||
|
|
@ -1919,6 +2051,16 @@ class routerHBP(HBSYSTEM):
|
|||
|
||||
|
||||
def dmrd_received(self, _peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data):
|
||||
|
||||
try:
|
||||
if CONFIG['SYSTEMS'][self._system]['_reset'] or CONFIG['SYSTEMS'][_system]['_reloadoptions']:
|
||||
if not CONFIG['SYSTEMS'][self._system]['_resetlog']:
|
||||
logger.info('(%s) disallow transmission until reset cycle is complete',_system)
|
||||
CONFIG['SYSTEMS'][self._system]['_resetlog'] = True
|
||||
return
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
pkt_time = time()
|
||||
dmrpkt = _data[20:53]
|
||||
|
||||
|
|
@ -2106,7 +2248,7 @@ class routerHBP(HBSYSTEM):
|
|||
|
||||
|
||||
|
||||
#Handle private voice calls (for reflectors)
|
||||
#Handle private voice calls (for dial-a-tg)
|
||||
elif _call_type == 'unit' and not _data_call and not self.STATUS[_slot]['_allStarMode']:
|
||||
if (_stream_id != self.STATUS[_slot]['RX_STREAM_ID']):
|
||||
|
||||
|
|
@ -2115,11 +2257,11 @@ class routerHBP(HBSYSTEM):
|
|||
|
||||
self.STATUS[_slot]['_stopTgAnnounce'] = False
|
||||
|
||||
logger.info('(%s) Reflector: Private call from %s to %s',self._system, int_id(_rf_src), _int_dst_id)
|
||||
logger.info('(%s) Dial-A-TG: Private call from %s to %s',self._system, int_id(_rf_src), _int_dst_id)
|
||||
if _int_dst_id >= 5 and _int_dst_id != 8 and _int_dst_id != 9 and _int_dst_id <= 999999:
|
||||
_bridgename = ''.join(['#',str(_int_dst_id)])
|
||||
if _bridgename not in BRIDGES and not (_int_dst_id >= 4000 and _int_dst_id <= 5000) and not (_int_dst_id >=9991 and _int_dst_id <= 9999):
|
||||
logger.info('(%s) [A] Reflector for TG %s does not exist. Creating as User Activated. Timeout: %s',self._system, _int_dst_id,CONFIG['SYSTEMS'][self._system]['DEFAULT_UA_TIMER'])
|
||||
logger.info('(%s) [A] Dial-A-TG for TG %s does not exist. Creating as User Activated. Timeout: %s',self._system, _int_dst_id,CONFIG['SYSTEMS'][self._system]['DEFAULT_UA_TIMER'])
|
||||
make_single_reflector(_dst_id,CONFIG['SYSTEMS'][self._system]['DEFAULT_UA_TIMER'],self._system)
|
||||
|
||||
if _int_dst_id > 5 and _int_dst_id != 9 and _int_dst_id != 5000 and not (_int_dst_id >=9991 and _int_dst_id <= 9999):
|
||||
|
|
@ -2132,7 +2274,7 @@ class routerHBP(HBSYSTEM):
|
|||
# TGID matches a rule source, reset its timer
|
||||
if _slot == _system['TS'] and _dst_id == _system['TGID'] and ((_system['TO_TYPE'] == 'ON' and (_system['ACTIVE'] == True)) or (_system['TO_TYPE'] == 'OFF' and _system['ACTIVE'] == False)):
|
||||
_system['TIMER'] = pkt_time + _system['TIMEOUT']
|
||||
logger.info('(%s) [B] Transmission match for Reflector: %s. Reset timeout to %s', self._system, _bridge, _system['TIMER'])
|
||||
logger.info('(%s) [B] Transmission match for Dial-A-TG: %s. Reset timeout to %s', self._system, _bridge, _system['TIMER'])
|
||||
|
||||
# TGID matches an ACTIVATION trigger
|
||||
if _int_dst_id == int(_dehash_bridge) and _system['SYSTEM'] == self._system and _slot == _system['TS']:
|
||||
|
|
@ -2140,15 +2282,15 @@ class routerHBP(HBSYSTEM):
|
|||
if _system['ACTIVE'] == False:
|
||||
_system['ACTIVE'] = True
|
||||
_system['TIMER'] = pkt_time + _system['TIMEOUT']
|
||||
logger.info('(%s) [C] Reflector: %s, connection changed to state: %s', self._system, _bridge, _system['ACTIVE'])
|
||||
logger.info('(%s) [C] Dial-A-TG: %s, connection changed to state: %s', self._system, _bridge, _system['ACTIVE'])
|
||||
# Cancel the timer if we've enabled an "OFF" type timeout
|
||||
if _system['TO_TYPE'] == 'OFF':
|
||||
_system['TIMER'] = pkt_time
|
||||
logger.info('(%s) [D] Reflector: %s has an "OFF" timer and set to "ON": timeout timer cancelled', self._system, _bridge)
|
||||
logger.info('(%s) [D] Dial-A-TG: %s has an "OFF" timer and set to "ON": timeout timer cancelled', self._system, _bridge)
|
||||
# Reset the timer for the rule
|
||||
if _system['ACTIVE'] == True and _system['TO_TYPE'] == 'ON':
|
||||
_system['TIMER'] = pkt_time + _system['TIMEOUT']
|
||||
logger.info('(%s) [E] Reflector: %s, timeout timer reset to: %s', self._system, _bridge, _system['TIMER'] - pkt_time)
|
||||
logger.info('(%s) [E] Dial-A-TG: %s, timeout timer reset to: %s', self._system, _bridge, _system['TIMER'] - pkt_time)
|
||||
|
||||
# TGID matches an DE-ACTIVATION trigger
|
||||
#Single TG mode
|
||||
|
|
@ -2158,19 +2300,19 @@ class routerHBP(HBSYSTEM):
|
|||
if _dst_id in _system['OFF'] or _int_dst_id != int(_dehash_bridge) :
|
||||
if _system['ACTIVE'] == True:
|
||||
_system['ACTIVE'] = False
|
||||
logger.info('(%s) [F] Reflector: %s, connection changed to state: %s', self._system, _bridge, _system['ACTIVE'])
|
||||
logger.info('(%s) [F] Dial-A-TG: %s, connection changed to state: %s', self._system, _bridge, _system['ACTIVE'])
|
||||
# Cancel the timer if we've enabled an "ON" type timeout
|
||||
if _system['TO_TYPE'] == 'ON':
|
||||
_system['TIMER'] = pkt_time
|
||||
logger.info('(%s) [G] Reflector: %s has ON timer and set to "OFF": timeout timer cancelled', self._system, _bridge)
|
||||
logger.info('(%s) [G] Dial-A-TG: %s has ON timer and set to "OFF": timeout timer cancelled', self._system, _bridge)
|
||||
# Reset the timer for the rule
|
||||
if _system['ACTIVE'] == False and _system['TO_TYPE'] == 'OFF':
|
||||
_system['TIMER'] = pkt_time + _system['TIMEOUT']
|
||||
logger.info('(%s) [H] Reflector: %s, timeout timer reset to: %s', self._system, _bridge, _system['TIMER'] - pkt_time)
|
||||
logger.info('(%s) [H] Dial-A-TG: %s, timeout timer reset to: %s', self._system, _bridge, _system['TIMER'] - pkt_time)
|
||||
# Cancel the timer if we've enabled an "ON" type timeout
|
||||
if _system['ACTIVE'] == True and _system['TO_TYPE'] == 'ON' and _dst_id in _system['OFF']:
|
||||
_system['TIMER'] = pkt_time
|
||||
logger.info('(%s) [I] Reflector: %s has ON timer and set to "OFF": timeout timer cancelled', self._system, _bridge)
|
||||
logger.info('(%s) [I] Dial-A-TG: %s has ON timer and set to "OFF": timeout timer cancelled', self._system, _bridge)
|
||||
|
||||
|
||||
if (_frame_type == HBPF_DATA_SYNC) and (_dtype_vseq == HBPF_SLT_VTERM) and (self.STATUS[_slot]['RX_TYPE'] != HBPF_SLT_VTERM):
|
||||
|
|
@ -2178,21 +2320,21 @@ class routerHBP(HBSYSTEM):
|
|||
_say = [words[_lang]['silence']]
|
||||
|
||||
if _int_dst_id < 8 or _int_dst_id == 9 :
|
||||
logger.info('(%s) Reflector: voice called - TG < 8 or 9 - "busy""', self._system)
|
||||
logger.info('(%s) Dial-A-TG: voice called - TG < 8 or 9 - "busy""', self._system)
|
||||
_say.append(words[_lang]['busy'])
|
||||
_say.append(words[_lang]['silence'])
|
||||
self.STATUS[_slot]['_stopTgAnnounce'] = True
|
||||
|
||||
#Allstar mode switch
|
||||
if CONFIG['ALLSTAR']['ENABLED'] and _int_dst_id == 8:
|
||||
logger.info('(%s) Reflector: voice called - TG 8 AllStar"', self._system)
|
||||
logger.info('(%s) Dial-A-TG: voice called - TG 8 AllStar"', self._system)
|
||||
_say.append(words[_lang]['all-star-link-mode'])
|
||||
_say.append(words[_lang]['silence'])
|
||||
self.STATUS[_slot]['_stopTgAnnounce'] = True
|
||||
self.STATUS[_slot]['_allStarMode'] = True
|
||||
reactor.callLater(30,resetallStarMode)
|
||||
elif not CONFIG['ALLSTAR']['ENABLED'] and _int_dst_id == 8:
|
||||
logger.info('(%s) Reflector: TG 8 AllStar not enabled"', self._system)
|
||||
logger.info('(%s) Dial-A-TG: TG 8 AllStar not enabled"', self._system)
|
||||
_say.append(words[_lang]['busy'])
|
||||
_say.append(words[_lang]['silence'])
|
||||
self.STATUS[_slot]['_stopTgAnnounce'] = True
|
||||
|
|
@ -2201,7 +2343,7 @@ class routerHBP(HBSYSTEM):
|
|||
|
||||
#If disconnection called
|
||||
if _int_dst_id == 4000:
|
||||
logger.info('(%s) Reflector: voice called - 4000 "not linked"', self._system)
|
||||
logger.info('(%s) Dial-A-TG: voice called - 4000 "not linked"', self._system)
|
||||
_say.append(words[_lang]['notlinked'])
|
||||
_say.append(words[_lang]['silence'])
|
||||
|
||||
|
|
@ -2215,7 +2357,7 @@ class routerHBP(HBSYSTEM):
|
|||
_dehash_bridge = _bridge[1:]
|
||||
if _system['SYSTEM'] == self._system and _slot == _system['TS']:
|
||||
if _system['ACTIVE'] == True:
|
||||
logger.info('(%s) Reflector: voice called - 5000 status - "linked to %s"', self._system,_dehash_bridge)
|
||||
logger.info('(%s) Dial-A-TG: voice called - 5000 status - "linked to %s"', self._system,_dehash_bridge)
|
||||
_say.append(words[_lang]['silence'])
|
||||
_say.append(words[_lang]['linkedto'])
|
||||
_say.append(words[_lang]['silence'])
|
||||
|
|
@ -2230,7 +2372,7 @@ class routerHBP(HBSYSTEM):
|
|||
break
|
||||
|
||||
if _active == False:
|
||||
logger.info('(%s) Reflector: voice called - 5000 status - "not linked"', self._system)
|
||||
logger.info('(%s) Dial-A-TG: voice called - 5000 status - "not linked"', self._system)
|
||||
_say.append(words[_lang]['notlinked'])
|
||||
|
||||
#Information services
|
||||
|
|
@ -2242,7 +2384,7 @@ class routerHBP(HBSYSTEM):
|
|||
|
||||
#Speak what TG was requested to link
|
||||
elif not self.STATUS[_slot]['_stopTgAnnounce']:
|
||||
logger.info('(%s) Reflector: voice called (linking) "linked to %s"', self._system,_int_dst_id)
|
||||
logger.info('(%s) Dial-A-TG: voice called (linking) "linked to %s"', self._system,_int_dst_id)
|
||||
_say.append(words[_lang]['silence'])
|
||||
_say.append(words[_lang]['linkedto'])
|
||||
_say.append(words[_lang]['silence'])
|
||||
|
|
@ -2556,12 +2698,17 @@ if __name__ == '__main__':
|
|||
import sys
|
||||
import os
|
||||
import signal
|
||||
|
||||
global CONFIG
|
||||
global KEYS
|
||||
keys = {}
|
||||
|
||||
# Higheset peer ID permitted by HBP
|
||||
PEER_MAX = 4294967295
|
||||
|
||||
ID_MAX = 16776415
|
||||
|
||||
|
||||
#Set process title early
|
||||
setproctitle(__file__)
|
||||
|
||||
|
|
@ -2571,7 +2718,7 @@ if __name__ == '__main__':
|
|||
# CLI argument parser - handles picking up the config file from the command line, and sending a "help" message
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-c', '--config', action='store', dest='CONFIG_FILE', help='/full/path/to/config.file (usually hblink.cfg)')
|
||||
parser.add_argument('-r', '--rules', action='store', dest='RULES_FILE', help='/full/path/to/rules.file (usually rules.py)')
|
||||
#parser.add_argument('-r', '--rules', action='store', dest='RULES_FILE', help='/full/path/to/rules.file (usually rules.py)')
|
||||
parser.add_argument('-l', '--logging', action='store', dest='LOG_LEVEL', help='Override config file logging level.')
|
||||
cli_args = parser.parse_args()
|
||||
|
||||
|
|
@ -2598,8 +2745,8 @@ if __name__ == '__main__':
|
|||
CONFIG = config.build_config(cli_args.CONFIG_FILE)
|
||||
|
||||
# Ensure we have a path for the rules file, if one wasn't specified, then use the default (top of file)
|
||||
if not cli_args.RULES_FILE:
|
||||
cli_args.RULES_FILE = os.path.dirname(os.path.abspath(__file__))+'/rules.py'
|
||||
#if not cli_args.RULES_FILE:
|
||||
# cli_args.RULES_FILE = os.path.dirname(os.path.abspath(__file__))+'/rules.py'
|
||||
|
||||
# Start the system logger
|
||||
if cli_args.LOG_LEVEL:
|
||||
|
|
@ -2621,13 +2768,33 @@ if __name__ == '__main__':
|
|||
logger.info('(GLOBAL) SHUTDOWN: CONFBRIDGE IS TERMINATING WITH SIGNAL %s', str(_signal))
|
||||
hblink_handler(_signal, _frame)
|
||||
logger.info('(GLOBAL) SHUTDOWN: ALL SYSTEM HANDLERS EXECUTED - STOPPING REACTOR')
|
||||
reactor.stop()
|
||||
if CONFIG['ALIASES']['SUB_MAP_FILE']:
|
||||
subMapWrite()
|
||||
if reactor.running:
|
||||
CONFIG['GLOBAL']['_KILL_SERVER'] = True
|
||||
else:
|
||||
exit()
|
||||
|
||||
|
||||
#Server kill routine
|
||||
def kill_server():
|
||||
try:
|
||||
if CONFIG['GLOBAL']['_KILL_SERVER']:
|
||||
logger.info('(GLOBAL) SHUTDOWN: CONFBRIDGE IS TERMINATING - killserver called')
|
||||
if reactor.running:
|
||||
reactor.stop()
|
||||
if CONFIG['ALIASES']['SUB_MAP_FILE']:
|
||||
subMapWrite()
|
||||
try:
|
||||
save_json(''.join([CONFIG['ALIASES']['PATH'], CONFIG['ALIASES']['KEYS_FILE']]),keys)
|
||||
logger.info('(KEYS) saved system keys to keystore')
|
||||
except Exception as e:
|
||||
logger.error('(GLOBAL) Canot save key file: %s',e)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
#install signal handlers
|
||||
signal.signal(signal.SIGTERM, sig_handler)
|
||||
signal.signal(signal.SIGINT, sig_handler)
|
||||
|
||||
# Set signal handers so that we can gracefully exit if need be
|
||||
for sig in [signal.SIGINT, signal.SIGTERM]:
|
||||
signal.signal(sig, sig_handler)
|
||||
|
||||
# Create the name-number mapping dictionaries
|
||||
peer_ids, subscriber_ids, talkgroup_ids, local_subscriber_ids, server_ids, checksums = mk_aliases(CONFIG)
|
||||
|
|
@ -2645,13 +2812,13 @@ if __name__ == '__main__':
|
|||
|
||||
|
||||
# Import the ruiles file as a module, and create BRIDGES from it
|
||||
spec = importlib.util.spec_from_file_location("module.name", cli_args.RULES_FILE)
|
||||
rules_module = importlib.util.module_from_spec(spec)
|
||||
try:
|
||||
spec.loader.exec_module(rules_module)
|
||||
logger.info('(ROUTER) Routing bridges file found and bridges imported: %s', cli_args.RULES_FILE)
|
||||
except (ImportError, FileNotFoundError):
|
||||
sys.exit('(ROUTER) TERMINATING: Routing bridges file not found or invalid: {}'.format(cli_args.RULES_FILE))
|
||||
#spec = importlib.util.spec_from_file_location("module.name", cli_args.RULES_FILE)
|
||||
#rules_module = importlib.util.module_from_spec(spec)
|
||||
#try:
|
||||
# spec.loader.exec_module(rules_module)
|
||||
# logger.info('(ROUTER) Routing bridges file found and bridges imported: %s', cli_args.RULES_FILE)
|
||||
#except (ImportError, FileNotFoundError):
|
||||
#sys.exit('(ROUTER) TERMINATING: Routing bridges file not found or invalid: {}'.format(cli_args.RULES_FILE))
|
||||
|
||||
#Load pickle of bridges if it's less than 25 seconds old
|
||||
#if os.path.isfile('bridge.pkl'):
|
||||
|
|
@ -2667,9 +2834,17 @@ if __name__ == '__main__':
|
|||
#BRIDGES = make_bridges(rules_module.BRIDGES)
|
||||
#os.unlink("bridge.pkl")
|
||||
#else:
|
||||
|
||||
BRIDGES = make_bridges(rules_module.BRIDGES)
|
||||
|
||||
|
||||
|
||||
if 'ECHO' in CONFIG['SYSTEMS'] and CONFIG['SYSTEMS']['ECHO']['MODE'] == 'PEER':
|
||||
BRIDGES = make_bridges({'9990': [{'SYSTEM': 'ECHO', 'TS': 2, 'TGID': 9990, 'ACTIVE': True, 'TIMEOUT': 2, 'TO_TYPE': 'NONE', 'ON': [], 'OFF': [], 'RESET': []},]})
|
||||
else:
|
||||
BRIDGES = {}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#Subscriber map for unit calls - complete with test entry
|
||||
#SUB_MAP = {bytes_3(73578):('REP-1',1,time())}
|
||||
SUB_MAP = {}
|
||||
|
|
@ -2697,7 +2872,7 @@ if __name__ == '__main__':
|
|||
_systemname = ''.join([system,'-',str(count)])
|
||||
generator[_systemname] = copy.deepcopy(CONFIG['SYSTEMS'][system])
|
||||
generator[_systemname]['PORT'] = generator[_systemname]['PORT'] + count
|
||||
generator[_systemname]['_default_options'] = "TS1_STATIC={};TS2_STATIC={};SINGLE={};DEFAULT_UA_TIMER={};DEFAULT_REFLECTOR={};VOICE={};LANG={}".format(generator[_systemname]['TS1_STATIC'],generator[_systemname]['TS2_STATIC'],int(generator[_systemname]['SINGLE_MODE']),generator[_systemname]['DEFAULT_UA_TIMER'],generator[_systemname]['DEFAULT_REFLECTOR'],int(generator[_systemname]['VOICE_IDENT']), generator[_systemname]['ANNOUNCEMENT_LANGUAGE'])
|
||||
generator[_systemname]['_default_options'] = "SINGLE={};DEFAULT_UA_TIMER={};VOICE={};LANG={}".format(int(generator[_systemname]['SINGLE_MODE']),generator[_systemname]['DEFAULT_UA_TIMER'],int(generator[_systemname]['VOICE_IDENT']), generator[_systemname]['ANNOUNCEMENT_LANGUAGE'])
|
||||
logger.debug('(GLOBAL) Generator - generated system %s',_systemname)
|
||||
generator[_systemname]['_default_options']
|
||||
systemdelete.append(system)
|
||||
|
|
@ -2709,13 +2884,15 @@ if __name__ == '__main__':
|
|||
|
||||
del generator
|
||||
del systemdelete
|
||||
|
||||
prohibitedTGs = [0,1,2,3,4,5,9,9990,9991,9992,9993,9994,9995,9996,9997,9998,9999]
|
||||
|
||||
# Default reflector
|
||||
logger.debug('(ROUTER) Setting default reflectors')
|
||||
logger.debug('(ROUTER) Setting default dial-a-tgs')
|
||||
for system in CONFIG['SYSTEMS']:
|
||||
if CONFIG['SYSTEMS'][system]['MODE'] != 'MASTER':
|
||||
continue
|
||||
if CONFIG['SYSTEMS'][system]['DEFAULT_REFLECTOR'] > 0:
|
||||
if CONFIG['SYSTEMS'][system]['DEFAULT_REFLECTOR'] not in prohibitedTGs:
|
||||
make_default_reflector(CONFIG['SYSTEMS'][system]['DEFAULT_REFLECTOR'],CONFIG['SYSTEMS'][system]['DEFAULT_UA_TIMER'],system)
|
||||
|
||||
#static TGs
|
||||
|
|
@ -2734,11 +2911,15 @@ if __name__ == '__main__':
|
|||
for tg in ts1:
|
||||
if not tg:
|
||||
continue
|
||||
if tg in prohibitedTGs:
|
||||
continue
|
||||
tg = int(tg)
|
||||
make_static_tg(tg,1,_tmout,system)
|
||||
for tg in ts2:
|
||||
if not tg:
|
||||
continue
|
||||
if tg in prohibitedTGs:
|
||||
continue
|
||||
tg = int(tg)
|
||||
make_static_tg(tg,2,_tmout,system)
|
||||
|
||||
|
|
@ -2782,6 +2963,9 @@ if __name__ == '__main__':
|
|||
logger.warning('(GLOBAL) Invalid language in ANNOUNCEMENT_LANGUAGE, skipping system %s',system)
|
||||
continue
|
||||
systems[system] = routerHBP(system, CONFIG, report_server)
|
||||
if (CONFIG['SYSTEMS'][system]['MODE'] == 'PEER' and system != 'ECHO') or CONFIG['SYSTEMS'][system]['MODE'] == 'XLXPEER':
|
||||
logger.warning('(GLOBAL) PEER and XLXPEER connections only allowed in bridge mode, skipping system %s',system)
|
||||
continue
|
||||
listeningPorts[system] = reactor.listenUDP(CONFIG['SYSTEMS'][system]['PORT'], systems[system], interface=CONFIG['SYSTEMS'][system]['IP'])
|
||||
logger.debug('(GLOBAL) %s instance created: %s, %s', CONFIG['SYSTEMS'][system]['MODE'], system, systems[system])
|
||||
|
||||
|
|
@ -2789,6 +2973,29 @@ if __name__ == '__main__':
|
|||
logger.error('(GLOBAL) STOPPING REACTOR TO AVOID MEMORY LEAK: Unhandled error in timed loop.\n %s', failure)
|
||||
reactor.stop()
|
||||
|
||||
#load keys if exists
|
||||
try:
|
||||
keys = load_json(''.join([CONFIG['ALIASES']['PATH'], CONFIG['ALIASES']['KEYS_FILE']]))
|
||||
except Exception as e:
|
||||
logger.error('(KEYS) Cannot load keys: %s',e)
|
||||
|
||||
#Initialize API
|
||||
if CONFIG['GLOBAL']['ENABLE_API']:
|
||||
api = config_API(CONFIG,BRIDGES)
|
||||
else:
|
||||
api = False
|
||||
if api:
|
||||
logger.info('(API) API running')
|
||||
if 'SYSTEM_API_KEY' not in keys or not keys['SYSTEM_API_KEY']:
|
||||
CONFIG['GLOBAL']['SYSTEM_API_KEY'] = secrets.token_hex(16)
|
||||
keys['SYSTEM_API_KEY'] = CONFIG['GLOBAL']['SYSTEM_API_KEY']
|
||||
logger.info('(API) Random system API Key generated: %s',CONFIG['GLOBAL']['SYSTEM_API_KEY'])
|
||||
else:
|
||||
CONFIG['GLOBAL']['SYSTEM_API_KEY'] = keys['SYSTEM_API_KEY']
|
||||
logger.info('(API) System API Key loaded from system key store')
|
||||
else:
|
||||
logger.info('(API) API not started')
|
||||
|
||||
# Initialize the rule timer -- this if for user activated stuff
|
||||
rule_timer_task = task.LoopingCall(rule_timer_loop)
|
||||
rule_timer = rule_timer_task.start(52)
|
||||
|
|
@ -2821,10 +3028,10 @@ if __name__ == '__main__':
|
|||
bridge = bridge_task.start(6)
|
||||
bridge.addErrback(loopingErrHandle)
|
||||
|
||||
#STAT trimmer - once every 10 mins (roughly - shifted so all timed tasks don't run at once
|
||||
#STAT trimmer - once every 5 mins (roughly - shifted so all timed tasks don't run at once
|
||||
if CONFIG['GLOBAL']['GEN_STAT_BRIDGES']:
|
||||
stat_trimmer_task = task.LoopingCall(statTrimmer)
|
||||
stat_trimmer = stat_trimmer_task.start(523)#3600
|
||||
stat_trimmer = stat_trimmer_task.start(303)#3600
|
||||
stat_trimmer.addErrback(loopingErrHandle)
|
||||
|
||||
#KA Reporting
|
||||
|
|
@ -2843,6 +3050,11 @@ if __name__ == '__main__':
|
|||
sub_trimmer_task = task.LoopingCall(SubMapTrimmer)
|
||||
sub_trimmer = sub_trimmer_task.start(3600)#3600
|
||||
sub_trimmer.addErrback(loopingErrHandle)
|
||||
|
||||
#Server kill switch checker
|
||||
killserver_task = task.LoopingCall(kill_server)
|
||||
killserver = killserver_task.start(5)
|
||||
killserver.addErrback(loopingErrHandle)
|
||||
|
||||
#more threads
|
||||
reactor.suggestThreadPoolSize(100)
|
||||
|
|
|
|||
|
|
@ -148,7 +148,9 @@ def build_config(_config_file):
|
|||
'SERVER_ID': config.getint(section, 'SERVER_ID', fallback=0).to_bytes(4, 'big'),
|
||||
'DATA_GATEWAY': config.getboolean(section, 'DATA_GATEWAY', fallback=False),
|
||||
'VALIDATE_SERVER_IDS': config.getboolean(section, 'VALIDATE_SERVER_IDS', fallback=True),
|
||||
'DEBUG_BRIDGES' : config.getboolean(section, 'DEBUG_BRIDGES', fallback=True)
|
||||
'DEBUG_BRIDGES' : config.getboolean(section, 'DEBUG_BRIDGES', fallback=False),
|
||||
'ENABLE_API' : config.getboolean(section, 'ENABLE_API', fallback=False)
|
||||
|
||||
|
||||
})
|
||||
if not CONFIG['GLOBAL']['ANNOUNCEMENT_LANGUAGES']:
|
||||
|
|
@ -187,9 +189,8 @@ def build_config(_config_file):
|
|||
'SERVER_ID_URL': config.get(section, 'SERVER_ID_URL', fallback='https://freedmr-lh.gb7fr.org.uk/json/server_ids.tsv'),
|
||||
'SERVER_ID_FILE': config.get(section, 'SERVER_ID_FILE', fallback='server_ids.tsv'),
|
||||
'CHECKSUM_URL': config.get(section, 'CHECKSUM_URL', fallback='https://freedmr-lh.gb7fr.org.uk/file_checksums.json'),
|
||||
'CHECKSUM_FILE': config.get(section, 'CHECKSUM_FILE', fallback='file_checksums.json')
|
||||
|
||||
|
||||
'CHECKSUM_FILE': config.get(section, 'CHECKSUM_FILE', fallback='file_checksums.json'),
|
||||
'KEYS_FILE': config.get(section, 'KEYS_FILE', fallback='keys.json')
|
||||
})
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -17,14 +17,16 @@
|
|||
###############################################################################
|
||||
FROM python:3.11-alpine
|
||||
|
||||
ENTRYPOINT [ "/entrypoint" ]
|
||||
#ENTRYPOINT [ "/entrypoint" ]
|
||||
ENTRYPOINT ["/sbin/tini", "-g", "--", "/entrypoint"]
|
||||
|
||||
COPY . /opt/freedmr
|
||||
|
||||
RUN addgroup -g 54000 freedmr && \
|
||||
adduser -D -u 54000 -G freedmr freedmr && \
|
||||
apk update && \
|
||||
apk add git gcc musl-dev && \
|
||||
apk add git gcc musl-dev supervisor && \
|
||||
apk add --no-cache tini && \
|
||||
cd /opt && \
|
||||
cd /opt/freedmr && \
|
||||
ls -lah && \
|
||||
|
|
@ -35,6 +37,7 @@ RUN addgroup -g 54000 freedmr && \
|
|||
chown -R freedmr:freedmr /run/priv_control
|
||||
|
||||
COPY docker-configs/entrypoint-proxy /entrypoint
|
||||
COPY docker-configs/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||
USER freedmr
|
||||
#
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -24,8 +24,6 @@ services:
|
|||
mem_reservation: 600m
|
||||
volumes:
|
||||
- '/etc/freedmr/freedmr.cfg:/opt/freedmr/freedmr.cfg'
|
||||
- '/var/log/freedmr/:/opt/freedmr/log/'
|
||||
- '/etc/freedmr/rules.py:/opt/freedmr/rules.py'
|
||||
#Write JSON files outside of container
|
||||
- '/etc/freedmr/json/:/opt/freedmr/json/'
|
||||
|
||||
|
|
|
|||
|
|
@ -176,8 +176,6 @@ TGID_TS2_ACL: PERMIT:ALL
|
|||
ANNOUNCEMENT_LANGUAGE: en_GB
|
||||
EOF
|
||||
|
||||
echo Install rules.py ...
|
||||
echo "BRIDGES = {'9990': [{'SYSTEM': 'ECHO', 'TS': 2, 'TGID': 9990, 'ACTIVE': True, 'TIMEOUT': 2, 'TO_TYPE': 'NONE', 'ON': [], 'OFF': [], 'RESET': []},]}" > /etc/freedmr/rules.py &&
|
||||
|
||||
echo Set perms on config directory...
|
||||
chown -R 54000 /etc/freedmr &&
|
||||
|
|
|
|||
|
|
@ -22,10 +22,11 @@ cd /opt/freedmr
|
|||
if [ "$BRIDGE_SERVER" == 1 ]
|
||||
then
|
||||
echo 'Starting in Bridge mode...'
|
||||
python /opt/freedmr/bridge.py -c freedmr.cfg -r rules.py
|
||||
exec python /opt/freedmr/bridge.py -c freedmr.cfg -r rules.py
|
||||
else
|
||||
echo 'Starting in FreeDMR mode...'
|
||||
python /opt/freedmr/hotspot_proxy_v2.py &
|
||||
python /opt/freedmr/playback.py -c loro.cfg &
|
||||
python /opt/freedmr/bridge_master.py -c freedmr.cfg -r rules.py
|
||||
exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
|
||||
#python /opt/freedmr/hotspot_proxy_v2.py &
|
||||
#python /opt/freedmr/playback.py -c loro.cfg &
|
||||
#exec python /opt/freedmr/bridge_master.py -c freedmr.cfg
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
[supervisord]
|
||||
nodaemon=true
|
||||
logfile=/dev/null
|
||||
logfile_maxbytes=0
|
||||
pidfile=/tmp/supervisord.pid
|
||||
|
||||
[program:freedmr]
|
||||
stdout_logfile=/dev/fd/1
|
||||
stdout_logfile_maxbytes=0
|
||||
redirect_stderr=true
|
||||
command=python /opt/freedmr/bridge_master.py -c freedmr.cfg
|
||||
stopwaitsecs=30
|
||||
autorestart=true
|
||||
priority=2
|
||||
|
||||
[program:proxy]
|
||||
stdout_logfile=/dev/fd/1
|
||||
stdout_logfile_maxbytes=0
|
||||
redirect_stderr=true
|
||||
command=python /opt/freedmr/hotspot_proxy_v2.py
|
||||
stopwaitsecs=30
|
||||
autorestart=true
|
||||
priority=1
|
||||
|
||||
[program:playback]
|
||||
stdout_logfile=/dev/fd/1
|
||||
stdout_logfile_maxbytes=0
|
||||
redirect_stderr=true
|
||||
command=/opt/freedmr/playback.py -c loro.cfg
|
||||
autorestart=true
|
||||
priority=3
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
#This empty config file will use defaults for everything apart from OBP and HBP config
|
||||
#This is usually a sensible choice.
|
||||
|
||||
|
||||
[GLOBAL]
|
||||
SERVER_ID: 0000
|
||||
DEBUG_BRIDGES: True
|
||||
ENABLE_API: True
|
||||
|
||||
[REPORTS]
|
||||
|
||||
[LOGGER]
|
||||
LOG_LEVEL: TRACE
|
||||
|
||||
[ALIASES]
|
||||
|
||||
[ALLSTAR]
|
||||
|
||||
[SYSTEM]
|
||||
MODE: MASTER
|
||||
ENABLED: True
|
||||
REPEAT: True
|
||||
MAX_PEERS: 1
|
||||
EXPORT_AMBE: False
|
||||
IP:
|
||||
PORT: 62031
|
||||
PASSPHRASE:
|
||||
GROUP_HANGTIME: 5
|
||||
USE_ACL: True
|
||||
REG_ACL: DENY:1
|
||||
SUB_ACL: DENY:1
|
||||
TGID_TS1_ACL: PERMIT:ALL
|
||||
TGID_TS2_ACL: PERMIT:ALL
|
||||
DEFAULT_UA_TIMER: 60
|
||||
SINGLE_MODE: True
|
||||
VOICE_IDENT: True
|
||||
TS1_STATIC:
|
||||
TS2_STATIC:
|
||||
DEFAULT_REFLECTOR: 0
|
||||
ANNOUNCEMENT_LANGUAGE: en_GB
|
||||
GENERATOR: 0
|
||||
ALLOW_UNREG_ID: False
|
||||
PROXY_CONTROL: True
|
||||
OVERRIDE_IDENT_TG:
|
||||
|
||||
[ECHO]
|
||||
MODE: PEER
|
||||
ENABLED: True
|
||||
LOOSE: False
|
||||
EXPORT_AMBE: False
|
||||
IP: 127.0.0.1
|
||||
PORT: 54916
|
||||
MASTER_IP: 127.0.0.1
|
||||
MASTER_PORT: 54915
|
||||
PASSPHRASE: passw0rd
|
||||
CALLSIGN: M0XFD
|
||||
RADIO_ID: 2340210
|
||||
RX_FREQ: 449000000
|
||||
TX_FREQ: 444000000
|
||||
TX_POWER: 25
|
||||
COLORCODE: 1
|
||||
SLOTS: 1
|
||||
LATITUDE: 00.0000
|
||||
LONGITUDE: 000.0000
|
||||
HEIGHT: 75
|
||||
LOCATION: United Kingdom
|
||||
DESCRIPTION: ECHO
|
||||
URL: www.w1abc.org
|
||||
SOFTWARE_ID: 20170620
|
||||
PACKAGE_ID: MMDVM_FreeDMR
|
||||
GROUP_HANGTIME: 5
|
||||
OPTIONS:
|
||||
USE_ACL: True
|
||||
SUB_ACL: DENY:1
|
||||
TGID_TS1_ACL: PERMIT:ALL
|
||||
TGID_TS2_ACL: PERMIT:ALL
|
||||
ANNOUNCEMENT_LANGUAGE: en_GB
|
||||
|
||||
|
|
@ -76,7 +76,7 @@ logging.trace = partial(logging.log, logging.TRACE)
|
|||
|
||||
# Does anybody read this stuff? There's a PEP somewhere that says I should do this.
|
||||
__author__ = 'Cortney T. Buffington, N0MJS, Forked by Simon Adlem - G7RZU'
|
||||
__copyright__ = 'Copyright (c) 2016-2019 Cortney T. Buffington, N0MJS and the K0USY Group, Simon Adlem, G7RZU 2020,2021,2022'
|
||||
__copyright__ = 'Copyright (c) 2016-2019 Cortney T. Buffington, N0MJS and the K0USY Group, Simon Adlem, G7RZU 2020,2021,2022,2023'
|
||||
__credits__ = 'Colin Durbridge, G4EML, Steve Zingman, N4IRS; Mike Zingman, N4IRR; Jonathan Naylor, G4KLX; Hans Barthen, DL5DI; Torsten Shultze, DG1HT; Jon Lee, G4TSN; Norman Williams, M6NBP'
|
||||
__license__ = 'GNU GPLv3'
|
||||
__maintainer__ = 'Simon Adlem G7RZU'
|
||||
|
|
@ -957,6 +957,7 @@ class HBSYSTEM(DatagramProtocol):
|
|||
self.transport.write(b''.join([MSTNAK, _peer_id]), _sockaddr)
|
||||
if self._config['PROXY_CONTROL']:
|
||||
self.proxy_IPBlackList(_peer_id,_sockaddr)
|
||||
self._CONFIG['SYSTEMS'][self._system]['_reset'] = True
|
||||
logger.warning('(%s) Invalid Login from %s Radio ID: %s Denied by Registation ACL or not registered ID', self._system, _sockaddr[0], int_id(_peer_id))
|
||||
else:
|
||||
self.transport.write(b''.join([MSTNAK, _peer_id]), _sockaddr)
|
||||
|
|
@ -1037,6 +1038,7 @@ class HBSYSTEM(DatagramProtocol):
|
|||
if self._config['PROXY_CONTROL']:
|
||||
self.proxy_IPBlackList(_peer_id,_sockaddr)
|
||||
self.transport.write(b''.join([MSTNAK, _peer_id]), _sockaddr)
|
||||
self._CONFIG['SYSTEMS'][self._system]['_reset'] = True
|
||||
logger.info('(%s) Callsign does not match subscriber database: ID: %s, Sent Call: %s, DB call %s', self._system, int_id(_peer_id),_this_peer['CALLSIGN'].decode('utf8').rstrip(),self.validate_id(_peer_id))
|
||||
else:
|
||||
self.send_peer(_peer_id, b''.join([RPTACK, _peer_id]))
|
||||
|
|
@ -1481,7 +1483,7 @@ def mk_aliases(_config):
|
|||
shutil.copy(_config['ALIASES']['PATH'] + _config['ALIASES']['SERVER_ID_FILE'],_config['ALIASES']['PATH'] + _config['ALIASES']['SERVER_ID_FILE'] + '.bak')
|
||||
except IOError as g:
|
||||
logger.info('(ALIAS) ID ALIAS MAPPER: couldn\'t make backup copy of server_ids file %s',g)
|
||||
|
||||
|
||||
|
||||
return peer_ids, subscriber_ids, talkgroup_ids, local_subscriber_ids, server_ids, checksums
|
||||
|
||||
|
|
|
|||
|
|
@ -60,28 +60,28 @@ class privHelper():
|
|||
with Pyro5.api.Proxy(self._netfilterURI) as nf:
|
||||
nf.blocklistAdd(dport,ip)
|
||||
except Exception as e:
|
||||
print('(PrivError) {}'.format(e))
|
||||
print('(PROXY)(PrivError) {}'.format(e))
|
||||
|
||||
def delBL(self,dport,ip):
|
||||
try:
|
||||
with Pyro5.api.Proxy(self._netfilterURI) as nf:
|
||||
nf.blocklistDel(dport,ip)
|
||||
except Exception as e:
|
||||
print('(PrivError) {}'.format(e))
|
||||
print('(PROXY)(PrivError) {}'.format(e))
|
||||
|
||||
def blocklistFlush(self):
|
||||
try:
|
||||
with Pyro5.api.Proxy(self._netfilterURI) as nf:
|
||||
nf.blocklistFlush()
|
||||
except Exception as e:
|
||||
print('(PrivError) {}'.format(e))
|
||||
print('(PROXY)(PrivError) {}'.format(e))
|
||||
|
||||
def flushCT(self):
|
||||
try:
|
||||
with Pyro5.api.Proxy(self._conntrackURI) as ct:
|
||||
ct.flushUDPTarget(62031)
|
||||
except Exception as e:
|
||||
print('(PrivError) {}'.format(e))
|
||||
print('(PROXY)(PrivError) {}'.format(e))
|
||||
|
||||
|
||||
class Proxy(DatagramProtocol):
|
||||
|
|
@ -168,9 +168,9 @@ class Proxy(DatagramProtocol):
|
|||
except KeyError:
|
||||
return
|
||||
if self.clientinfo:
|
||||
print('Add to blacklist: host {}. Expire time {}'.format(self.peerTrack[_peer_id]['shost'],_bltime))
|
||||
print('(PROXY)Add to blacklist: host {}. Expire time {}'.format(self.peerTrack[_peer_id]['shost'],_bltime))
|
||||
if self.privHelper:
|
||||
print('Ask priv_helper to add to iptables: host {}, port {}.'.format(self.peerTrack[_peer_id]['shost'],self.ListenPort))
|
||||
print('(PROXY)Ask priv_helper to add to iptables: host {}, port {}.'.format(self.peerTrack[_peer_id]['shost'],self.ListenPort))
|
||||
reactor.callInThread(self.privHelper.addBL,self.ListenPort,self.peerTrack[_peer_id]['shost'])
|
||||
return
|
||||
|
||||
|
|
@ -218,15 +218,15 @@ class Proxy(DatagramProtocol):
|
|||
self.rptlTrack[host] += 1
|
||||
|
||||
if self.rptlTrack[host] > 20:
|
||||
print('(RPTL) exceeded max: {}'.format(self.rptlTrack[host]))
|
||||
print('(PROXY)(RPTL) exceeded max: {}'.format(self.rptlTrack[host]))
|
||||
_bltime = nowtime + 600
|
||||
self.IPBlackList[host] = _bltime
|
||||
self.rptlTrack.pop(host)
|
||||
|
||||
if self.clientinfo:
|
||||
print('(RPTL) Add to blacklist: host {}. Expire time {}'.format(host,_bltime))
|
||||
print('(PROXY)(RPTL) Add to blacklist: host {}. Expire time {}'.format(host,_bltime))
|
||||
if self.privHelper:
|
||||
print('(RPTL) Ask priv_helper to add to iptables: host {}, port {}.'.format(host,self.ListenPort))
|
||||
print('(PROXY)(RPTL) Ask priv_helper to add to iptables: host {}, port {}.'.format(host,self.ListenPort))
|
||||
reactor.callInThread(self.privHelper.addBL,self.ListenPort,host)
|
||||
return
|
||||
|
||||
|
|
@ -314,7 +314,7 @@ if __name__ == '__main__':
|
|||
config = configparser.ConfigParser()
|
||||
|
||||
if not config.read(_config_file):
|
||||
print('Configuration file \''+_config_file+'\' is not a valid configuration file!')
|
||||
print('(PROXY)Configuration file \''+_config_file+'\' is not a valid configuration file!')
|
||||
|
||||
try:
|
||||
|
||||
|
|
@ -331,9 +331,9 @@ if __name__ == '__main__':
|
|||
IPBlackList = json.loads(config.get('PROXY','IPBlackList'))
|
||||
|
||||
except configparser.Error as err:
|
||||
print('Error processing configuration file -- {}'.format(err))
|
||||
print('(PROXY)Error processing configuration file -- {}'.format(err))
|
||||
|
||||
print('Using default config')
|
||||
print('(PROXY)Using default config')
|
||||
#*** CONFIG HERE ***
|
||||
|
||||
Master = "127.0.0.1"
|
||||
|
|
@ -345,7 +345,7 @@ if __name__ == '__main__':
|
|||
Timeout = 30
|
||||
Stats = False
|
||||
Debug = False
|
||||
ClientInfo = False
|
||||
ClientInfo = True
|
||||
BlackList = [1234567]
|
||||
#e.g. {10.0.0.1: 0, 10.0.0.2: 0}
|
||||
IPBlackList = {}
|
||||
|
|
@ -359,19 +359,22 @@ if __name__ == '__main__':
|
|||
|
||||
# Set up the signal handler
|
||||
def sig_handler(_signal, _frame):
|
||||
print('(GLOBAL) SHUTDOWN: PROXY IS TERMINATING WITH SIGNAL {}'.format(str(_signal)))
|
||||
print('(PROXY)(GLOBAL) SHUTDOWN: PROXY IS TERMINATING WITH SIGNAL {}'.format(str(_signal)))
|
||||
reactor.stop()
|
||||
|
||||
# Set signal handers so that we can gracefully exit if need be
|
||||
for sig in [signal.SIGINT, signal.SIGTERM]:
|
||||
signal.signal(sig, sig_handler)
|
||||
def sigt(_signal,_frame):
|
||||
print('oooh')
|
||||
|
||||
#Install signal handlers
|
||||
signal.signal(signal.SIGINT, sig_handler)
|
||||
signal.signal(signal.SIGTERM, sigt)
|
||||
|
||||
#readState()
|
||||
|
||||
#If IPv6 is enabled by enivornment variable...
|
||||
if ListenIP == '' and 'FDPROXY_IPV6' in os.environ and bool(os.environ['FDPROXY_IPV6']):
|
||||
ListenIP = '::'
|
||||
|
||||
|
||||
#Override static config from Environment
|
||||
if 'FDPROXY_STATS' in os.environ:
|
||||
Stats = bool(os.environ['FDPROXY_STATS'])
|
||||
|
|
@ -385,11 +388,11 @@ if __name__ == '__main__':
|
|||
unixSocket = '/run/priv_control/priv_control.unixsocket'
|
||||
|
||||
if os.path.exists(unixSocket) and stat.S_ISSOCK(os.stat(unixSocket).st_mode):
|
||||
print('(PRIV) Found UNIX socket. Enabling priv helper')
|
||||
print('(PROXY)(PRIV) Found UNIX socket. Enabling priv helper')
|
||||
PRIV_HELPER = privHelper()
|
||||
print('(PRIV) flush conntrack')
|
||||
print('(PROXY)(PRIV) flush conntrack')
|
||||
PRIV_HELPER.flushCT()
|
||||
print('(PRIV) flush blocklist')
|
||||
print('(PROXY)(PRIV) flush blocklist')
|
||||
PRIV_HELPER.blocklistFlush()
|
||||
|
||||
|
||||
|
|
@ -404,7 +407,7 @@ if __name__ == '__main__':
|
|||
reactor.listenUDP(ListenPort,Proxy(Master,ListenPort,CONNTRACK,PEERTRACK,BlackList,IPBlackList,Timeout,Debug,ClientInfo,DestportStart,DestPortEnd,PRIV_HELPER, RPTLTRACK),interface=ListenIP)
|
||||
|
||||
def loopingErrHandle(failure):
|
||||
print('(GLOBAL) STOPPING REACTOR TO AVOID MEMORY LEAK: Unhandled error innowtimed loop.\n {}'.format(failure))
|
||||
print('(PROXY)(GLOBAL) STOPPING REACTOR TO AVOID MEMORY LEAK: Unhandled error innowtimed loop.\n {}'.format(failure))
|
||||
reactor.stop()
|
||||
|
||||
def stats():
|
||||
|
|
@ -430,14 +433,14 @@ if __name__ == '__main__':
|
|||
for delete in _dellist:
|
||||
IPBlackList.pop(delete)
|
||||
if ClientInfo:
|
||||
print('Remove dynamic blacklist entry for {}'.format(delete))
|
||||
print('(PROXY)Remove dynamic blacklist entry for {}'.format(delete))
|
||||
if PRIV_HELPER:
|
||||
print('Ask priv helper to remove blacklist entry for {} from iptables'.format(delete))
|
||||
print('(PROXY)Ask priv helper to remove blacklist entry for {} from iptables'.format(delete))
|
||||
reactor.callInThread(PRIV_HELPER.delBL,ListenPort,delete)
|
||||
|
||||
def rptlTrimmer():
|
||||
RPTLTRACK.clear()
|
||||
print('Purge RPTL table')
|
||||
print('(PROXY)Purge RPTL table')
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -264,6 +264,8 @@ voiceMap = {
|
|||
'de_DE': {
|
||||
|
||||
'to': 'silence',
|
||||
'freedmr': 'silence',
|
||||
'this-is': 'silence',
|
||||
'allstar-link-mode': 'A'
|
||||
},
|
||||
|
||||
|
|
|
|||
6
loro.cfg
6
loro.cfg
|
|
@ -92,9 +92,9 @@ REPORT_CLIENTS: 127.0.0.1
|
|||
#
|
||||
[LOGGER]
|
||||
LOG_FILE: /dev/null
|
||||
LOG_HANDLERS: null
|
||||
LOG_LEVEL: DEBUG
|
||||
LOG_NAME: HBlink
|
||||
LOG_HANDLERS: console-timed
|
||||
LOG_LEVEL: INFO
|
||||
LOG_NAME: ECHO
|
||||
|
||||
# DOWNLOAD AND IMPORT SUBSCRIBER, PEER and TGID ALIASES
|
||||
# Ok, not the TGID, there's no master list I know of to download
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
home = /usr/bin
|
||||
include-system-site-packages = false
|
||||
version = 3.10.12
|
||||
|
|
@ -6,3 +6,4 @@ configparser>=3.0.0
|
|||
resettabletimer>=0.7.0
|
||||
setproctitle
|
||||
Pyro5
|
||||
spyne
|
||||
|
|
|
|||
12
utils.py
12
utils.py
|
|
@ -26,7 +26,7 @@ import ssl
|
|||
from time import time
|
||||
from os.path import isfile, getmtime
|
||||
from urllib.request import urlopen
|
||||
from json import load as jload
|
||||
from json import load as jload, dump as jdump
|
||||
import hashlib
|
||||
|
||||
|
||||
|
|
@ -92,6 +92,16 @@ def load_json(filename):
|
|||
else:
|
||||
return(data)
|
||||
|
||||
def save_json(filename,data):
|
||||
try:
|
||||
with open(filename, 'w', encoding='utf-8') as f:
|
||||
jdump(data, f, ensure_ascii=False, indent=4)
|
||||
except:
|
||||
raise
|
||||
else:
|
||||
return(True)
|
||||
|
||||
|
||||
|
||||
#Calculate blake2b checksum of file
|
||||
def blake2bsum(filename):
|
||||
|
|
|
|||
Loading…
Reference in New Issue