fix: health check repairs dropped monitors even when pineapd is healthy

wifi reloads during attack deploy/stop drop the monitor interfaces and
pineapd only recovers its primary; the monitor now brings both up
whenever it finds them down, with or without a pineapd failure.
This commit is contained in:
2026-08-18 21:33:11 -05:00
parent dcab92bd11
commit d0ee8b7ff4
2 changed files with 37 additions and 20 deletions
@@ -3403,8 +3403,7 @@ def health_check():
h = _health h = _health
rc, out, err = device_run(['pidof', 'pineapd'], timeout=10) rc, out, err = device_run(['pidof', 'pineapd'], timeout=10)
h['pineap_up'] = rc == 0 and bool((out or '').strip()) h['pineap_up'] = rc == 0 and bool((out or '').strip())
if h['pineap_up']: if not h['pineap_up']:
return dict(h)
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)
@@ -3414,10 +3413,7 @@ def health_check():
device_run(['uci', 'commit', 'pineapd']) device_run(['uci', 'commit', 'pineapd'])
h['last_action'] = 'SSID pool broadcast disabled (pineapd crash-loop fix)' h['last_action'] = 'SSID pool broadcast disabled (pineapd crash-loop fix)'
elif _monitor_down('wlan1mon') or _monitor_down('wlan0mon'): elif _monitor_down('wlan1mon') or _monitor_down('wlan0mon'):
for name in ('wlan1mon', 'wlan0mon'): _bring_monitors_up(h)
if _monitor_down(name):
device_run(['ip', 'link', 'set', name, 'up'], timeout=10)
h['last_action'] = 'monitor interfaces brought up'
else: else:
h['last_action'] = _stabilize_pineapd() h['last_action'] = _stabilize_pineapd()
device_run(['/etc/init.d/pineapd', 'restart'], timeout=30) device_run(['/etc/init.d/pineapd', 'restart'], timeout=30)
@@ -3425,6 +3421,19 @@ def health_check():
h['last_fix'] = now h['last_fix'] = now
h['fixes'] += 1 h['fixes'] += 1
return dict(h) return dict(h)
# pineapd is healthy, but wifi reloads still drop the monitors (pineapd
# does not bring secondary monitors back). Repair them without cooldown.
if _monitor_down('wlan1mon') or _monitor_down('wlan0mon'):
_bring_monitors_up(h)
return dict(h)
def _bring_monitors_up(h):
for name in ('wlan1mon', 'wlan0mon'):
if _monitor_down(name):
device_run(['ip', 'link', 'set', name, 'up'], timeout=10)
h['last_action'] = 'monitor interfaces brought up'
h['monitor_fixes'] = h.get('monitor_fixes', 0) + 1
def h_health(ctx): def h_health(ctx):
+8
View File
@@ -59,6 +59,14 @@ class HealthCheckTest(unittest.TestCase):
self.assertEqual([r[0] for r in self.runs], [['pidof', 'pineapd']], self.assertEqual([r[0] for r in self.runs], [['pidof', 'pineapd']],
'health check must not write to the pineapd socket') 'health check must not write to the pineapd socket')
def test_pineap_up_repairs_dropped_monitors(self):
self.iface_up = {'wlan0mon': False, 'wlan1mon': True}
result = server.health_check()
self.assertTrue(result['pineap_up'])
self.assertEqual(result['last_action'], 'monitor interfaces brought up')
self.assertIn(['ip', 'link', 'set', 'wlan0mon', 'up'], [r[0] for r in self.runs])
self.assertEqual(result['monitor_fixes'], 1)
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