78 lines
2.7 KiB
Python
78 lines
2.7 KiB
Python
"""Boot-time reconciliation of crash-prone PineAP settings."""
|
|
import time
|
|
|
|
from server import (_apply_uci_wanted, _monitor_down, _raise_monitors,
|
|
PINEAPD_SAFE_UCI, device_run)
|
|
|
|
WANTED_EXTRA = {'pineapd.@pineapd[0].autossidpool': '0'}
|
|
POOL_CLEAR_MAX = 20
|
|
MONITORS = ('wlan0mon', 'wlan1mon')
|
|
# pineapd.wlan1mon.hop is intentionally NOT part of the applied set: channel
|
|
# hopping is owned by the RF role manager (_pause_hop/_resume_hop). Hop=1 is
|
|
# the healthy recon baseline, so reconciling it here would silently disable a
|
|
# pager-enabled setting at every boot and latch GUARD PENDING after any
|
|
# attack-role switch.
|
|
GR_TTL_SECONDS = 30
|
|
_GR_CACHE = {'t': 0.0, 'data': None}
|
|
|
|
|
|
def _wanted():
|
|
wanted = {k: v for k, v in PINEAPD_SAFE_UCI.items()
|
|
if k != 'pineapd.wlan1mon.hop'}
|
|
wanted.update(WANTED_EXTRA)
|
|
return wanted
|
|
|
|
|
|
def _pool_size():
|
|
rc, out, err = device_run(
|
|
['uci', 'get', 'pineapd.@ssidpool[0].ssid'])
|
|
if rc != 0 or not (out or '').strip():
|
|
return 0
|
|
return len(out.split())
|
|
|
|
|
|
def _ensure_pineapd_section():
|
|
"""Stock daemon rewrites and profile restores can drop the whole
|
|
`config pineapd` section; every @pineapd[0] option write fails with
|
|
'Invalid argument' until it exists again. Probes SECTION existence
|
|
(`uci -q show @pineapd[0]`) — never an option, which may legitimately
|
|
be absent from a rewritten section."""
|
|
rc, out, err = device_run(['uci', '-q', 'show', 'pineapd.@pineapd[0]'])
|
|
if rc == 0:
|
|
return False
|
|
device_run(['uci', 'add', 'pineapd', 'pineapd'])
|
|
device_run(['uci', 'commit', 'pineapd'])
|
|
return True
|
|
|
|
|
|
def reconcile(clear_pool=True):
|
|
changed = []
|
|
if _ensure_pineapd_section():
|
|
changed.append('pineapd.@pineapd[0] (section recreated)')
|
|
changed += _apply_uci_wanted(_wanted())
|
|
pool_cleared = False
|
|
if clear_pool and _pool_size() > POOL_CLEAR_MAX:
|
|
device_run(['uci', 'delete', 'pineapd.@ssidpool[0].ssid'])
|
|
pool_cleared = True
|
|
if changed or pool_cleared:
|
|
device_run(['uci', 'commit', 'pineapd'])
|
|
raised = _raise_monitors() if any(_monitor_down(m) for m in MONITORS) else []
|
|
_GR_CACHE['data'] = None
|
|
return {'changed': changed, 'pool_cleared': pool_cleared,
|
|
'monitors_raised': raised}
|
|
|
|
|
|
def guard_report():
|
|
now = time.time()
|
|
cached = _GR_CACHE['data']
|
|
if cached is not None and now - _GR_CACHE['t'] < GR_TTL_SECONDS:
|
|
return cached
|
|
from server import _pending_uci
|
|
_ensure_pineapd_section()
|
|
pending = _pending_uci(_wanted())
|
|
report = {'in_sync': not pending, 'pending': pending,
|
|
'pool_size': _pool_size()}
|
|
_GR_CACHE['t'] = now
|
|
_GR_CACHE['data'] = report
|
|
return report
|