fix: health monitor disables SSID pool immediately on PING failure
Ring-buffer SIGSEGV counts were unreliable for growth detection; the pool broadcast is the only known crash cause, so disable it on first failure.
This commit is contained in:
@@ -3205,7 +3205,11 @@ def _monitor_down(name):
|
|||||||
|
|
||||||
def health_check():
|
def health_check():
|
||||||
"""One health pass. Returns the health dict. Fix actions are
|
"""One health pass. Returns the health dict. Fix actions are
|
||||||
rate-limited by HEALTH_FIX_COOLDOWN."""
|
rate-limited by HEALTH_FIX_COOLDOWN.
|
||||||
|
|
||||||
|
The stock SSID-pool broadcast segfaults pineapd on this firmware; when
|
||||||
|
pineapd is unreachable the pool broadcast is disabled before restarting,
|
||||||
|
since it is the only known crash cause."""
|
||||||
h = _health
|
h = _health
|
||||||
rc, out, err = device_run([HAK5CMD, 'PING'], timeout=10)
|
rc, out, err = device_run([HAK5CMD, 'PING'], timeout=10)
|
||||||
h['pineap_up'] = rc == 0 and 'PONG' in (out or '')
|
h['pineap_up'] = rc == 0 and 'PONG' in (out or '')
|
||||||
@@ -3214,20 +3218,18 @@ def health_check():
|
|||||||
now = time.time()
|
now = time.time()
|
||||||
if now - h['last_fix'] < HEALTH_FIX_COOLDOWN:
|
if now - h['last_fix'] < HEALTH_FIX_COOLDOWN:
|
||||||
return dict(h)
|
return dict(h)
|
||||||
count = _sigsegv_count()
|
pool = _uci_section('pineapd.@ssidpool[0]')
|
||||||
if h['sigsegv_last'] is not None and count > h['sigsegv_last']:
|
if pool.get('disable') != '1':
|
||||||
# Crash-loop signature: disable the SSID pool broadcast and restart.
|
|
||||||
device_run(['uci', 'set', 'pineapd.@ssidpool[0].disable=1'])
|
device_run(['uci', 'set', 'pineapd.@ssidpool[0].disable=1'])
|
||||||
device_run(['uci', 'commit', 'pineapd'])
|
device_run(['uci', 'commit', 'pineapd'])
|
||||||
device_run(['/etc/init.d/pineapd', 'restart'], timeout=30)
|
h['last_action'] = 'SSID pool broadcast disabled (pineapd crash-loop fix)'
|
||||||
h['last_action'] = 'pool-disabled + pineapd restart (SIGSEGV crash-loop)'
|
|
||||||
elif _monitor_down('wlan1mon'):
|
elif _monitor_down('wlan1mon'):
|
||||||
device_run(['ip', 'link', 'set', 'wlan1mon', 'up'], timeout=10)
|
device_run(['ip', 'link', 'set', 'wlan1mon', 'up'], timeout=10)
|
||||||
h['last_action'] = 'wlan1mon brought up'
|
h['last_action'] = 'wlan1mon brought up'
|
||||||
else:
|
else:
|
||||||
device_run(['/etc/init.d/pineapd', 'restart'], timeout=30)
|
|
||||||
h['last_action'] = 'pineapd restart'
|
h['last_action'] = 'pineapd restart'
|
||||||
h['sigsegv_last'] = count
|
device_run(['/etc/init.d/pineapd', 'restart'], timeout=30)
|
||||||
|
h['sigsegv_last'] = _sigsegv_count()
|
||||||
h['last_fix'] = now
|
h['last_fix'] = now
|
||||||
h['fixes'] += 1
|
h['fixes'] += 1
|
||||||
return dict(h)
|
return dict(h)
|
||||||
|
|||||||
+10
-3
@@ -49,15 +49,22 @@ class HealthCheckTest(unittest.TestCase):
|
|||||||
def test_down_with_growing_sigsegv_disables_pool(self):
|
def test_down_with_growing_sigsegv_disables_pool(self):
|
||||||
self.ping_ok = False
|
self.ping_ok = False
|
||||||
self.sigsegs = 5
|
self.sigsegs = 5
|
||||||
server._health['sigsegv_last'] = 3
|
|
||||||
result = server.health_check()
|
result = server.health_check()
|
||||||
self.assertIn('pool-disabled', result['last_action'])
|
self.assertIn('pool broadcast disabled', result['last_action'])
|
||||||
self.assertEqual(self.uci_state['pineapd.@ssidpool[0].disable'], '1')
|
self.assertEqual(self.uci_state['pineapd.@ssidpool[0].disable'], '1')
|
||||||
self.assertIn(['/etc/init.d/pineapd', 'restart'], [r[0] for r in self.runs])
|
self.assertIn(['/etc/init.d/pineapd', 'restart'], [r[0] for r in self.runs])
|
||||||
self.assertEqual(result['fixes'], 1)
|
self.assertEqual(result['fixes'], 1)
|
||||||
|
|
||||||
|
def test_down_with_pool_already_disabled_restarts_pineapd(self):
|
||||||
|
self.ping_ok = False
|
||||||
|
self.uci_state['pineapd.@ssidpool[0].disable'] = '1'
|
||||||
|
result = server.health_check()
|
||||||
|
self.assertEqual(result['last_action'], 'pineapd restart')
|
||||||
|
self.assertIn(['/etc/init.d/pineapd', 'restart'], [r[0] for r in self.runs])
|
||||||
|
|
||||||
def test_down_without_crash_brings_monitor_up(self):
|
def test_down_without_crash_brings_monitor_up(self):
|
||||||
self.ping_ok = False
|
self.ping_ok = False
|
||||||
|
self.uci_state['pineapd.@ssidpool[0].disable'] = '1'
|
||||||
self.iw_out = 'wlan0mon\n'
|
self.iw_out = 'wlan0mon\n'
|
||||||
result = server.health_check()
|
result = server.health_check()
|
||||||
self.assertEqual(result['last_action'], 'wlan1mon brought up')
|
self.assertEqual(result['last_action'], 'wlan1mon brought up')
|
||||||
@@ -67,7 +74,7 @@ class HealthCheckTest(unittest.TestCase):
|
|||||||
self.ping_ok = False
|
self.ping_ok = False
|
||||||
server._health['sigsegv_last'] = 4
|
server._health['sigsegv_last'] = 4
|
||||||
result = server.health_check()
|
result = server.health_check()
|
||||||
self.assertEqual(result['last_action'], 'pineapd restart')
|
self.assertIn('pineapd restart', result['last_action'])
|
||||||
self.assertIn(['/etc/init.d/pineapd', 'restart'], [r[0] for r in self.runs])
|
self.assertIn(['/etc/init.d/pineapd', 'restart'], [r[0] for r in self.runs])
|
||||||
|
|
||||||
def test_fix_cooldown_prevents_thrash(self):
|
def test_fix_cooldown_prevents_thrash(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user