diff --git a/payload/user/remote_access/pager-webui/server.py b/payload/user/remote_access/pager-webui/server.py index 1647199..2bd4a73 100644 --- a/payload/user/remote_access/pager-webui/server.py +++ b/payload/user/remote_access/pager-webui/server.py @@ -3403,30 +3403,39 @@ def health_check(): h = _health rc, out, err = device_run(['pidof', 'pineapd'], timeout=10) h['pineap_up'] = rc == 0 and bool((out or '').strip()) - if h['pineap_up']: + if not h['pineap_up']: + now = time.time() + if now - h['last_fix'] < HEALTH_FIX_COOLDOWN: + return dict(h) + pool = _uci_section('pineapd.@ssidpool[0]') + if pool.get('disable') != '1': + device_run(['uci', 'set', 'pineapd.@ssidpool[0].disable=1']) + device_run(['uci', 'commit', 'pineapd']) + h['last_action'] = 'SSID pool broadcast disabled (pineapd crash-loop fix)' + elif _monitor_down('wlan1mon') or _monitor_down('wlan0mon'): + _bring_monitors_up(h) + else: + h['last_action'] = _stabilize_pineapd() + device_run(['/etc/init.d/pineapd', 'restart'], timeout=30) + h['sigsegv_last'] = _sigsegv_count() + h['last_fix'] = now + h['fixes'] += 1 return dict(h) - now = time.time() - if now - h['last_fix'] < HEALTH_FIX_COOLDOWN: - return dict(h) - pool = _uci_section('pineapd.@ssidpool[0]') - if pool.get('disable') != '1': - device_run(['uci', 'set', 'pineapd.@ssidpool[0].disable=1']) - device_run(['uci', 'commit', 'pineapd']) - h['last_action'] = 'SSID pool broadcast disabled (pineapd crash-loop fix)' - elif _monitor_down('wlan1mon') or _monitor_down('wlan0mon'): - 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' - else: - h['last_action'] = _stabilize_pineapd() - device_run(['/etc/init.d/pineapd', 'restart'], timeout=30) - h['sigsegv_last'] = _sigsegv_count() - h['last_fix'] = now - h['fixes'] += 1 + # 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): h = dict(_health) h['sigsegv_count'] = h.pop('sigsegv_last') diff --git a/tests/test_health.py b/tests/test_health.py index 591c336..954cefe 100644 --- a/tests/test_health.py +++ b/tests/test_health.py @@ -59,6 +59,14 @@ class HealthCheckTest(unittest.TestCase): self.assertEqual([r[0] for r in self.runs], [['pidof', 'pineapd']], '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): self.ping_ok = False self.sigsegs = 5