diff --git a/payload/user/remote_access/pager-webui/server.py b/payload/user/remote_access/pager-webui/server.py index 8500ef3..1647199 100644 --- a/payload/user/remote_access/pager-webui/server.py +++ b/payload/user/remote_access/pager-webui/server.py @@ -3347,10 +3347,11 @@ def _stabilize_pineapd(): 1. SSID-pool broadcast (segfault, ~15s cadence) 2. wlan2mon: a 6GHz monitor this hardware never creates; hopping the missing iface segfaults pineapd - 3. wlan1mon fast-hopping the 6GHz channel list (ASIO io thread exit) - The pool list itself is also cleared: collect refills it, and a large - pool was observed crashing even with broadcast disabled. - Returns a human-readable description of what was changed.""" + 3. wlan1mon fast-hopping (stalls the command socket; the stock daemon's + watchdog then SIGTERMs pineapd every ~30s) + 4. a large refilled pool (collect refills it; crashes observed even + with broadcast disabled) + The pool list itself is cleared. Returns what changed.""" actions = [] wanted = { 'pineapd.@ssidpool[0].disable': '1', @@ -3358,6 +3359,7 @@ def _stabilize_pineapd(): 'pineapd.wlan2mon.hop': '0', 'pineapd.wlan1mon.bands': '5', 'pineapd.wlan0mon.bands': '2', + 'pineapd.wlan1mon.hop': '0', } for key, value in wanted.items(): rc, out, err = device_run(['uci', 'get', key]) @@ -3394,12 +3396,13 @@ def health_check(): """One health pass. Returns the health dict. Fix actions are 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.""" + The check is PASSIVE (pidof) — actively pinging pineapd's command + socket every 15s collides with the stock daemon's own socket writes + ('[PineAp] Error writing' -> daemon watchdog SIGTERMs pineapd). + """ h = _health - rc, out, err = device_run([HAK5CMD, 'PING'], timeout=10) - h['pineap_up'] = rc == 0 and 'PONG' in (out or '') + rc, out, err = device_run(['pidof', 'pineapd'], timeout=10) + h['pineap_up'] = rc == 0 and bool((out or '').strip()) if h['pineap_up']: return dict(h) now = time.time() diff --git a/tests/test_health.py b/tests/test_health.py index a88c859..591c336 100644 --- a/tests/test_health.py +++ b/tests/test_health.py @@ -26,10 +26,10 @@ class HealthCheckTest(unittest.TestCase): def fake_run(args, timeout=20, input_data=None): self.runs.append((list(args), timeout)) a = list(args) - if a[0] == '/usr/bin/hak5cmd' and a[1] == 'PING': + if a[0] == 'pidof' and a[1] == 'pineapd': if self.ping_ok: - return (0, 'Sending PING...\nGot PONG response\n', '') - return (1, '', 'could not connect to pineap: connection refused') + return (0, '12345\n', '') + return (1, '', '') if a[0] == 'logread': return (0, 'SIGSEGV\n' * self.sigsegs if hasattr(self, 'sigsegs') else '', '') if a[:2] == ['uci', 'set']: @@ -56,6 +56,8 @@ class HealthCheckTest(unittest.TestCase): result = server.health_check() self.assertTrue(result['pineap_up']) self.assertIsNone(result['last_action']) + self.assertEqual([r[0] for r in self.runs], [['pidof', 'pineapd']], + 'health check must not write to the pineapd socket') def test_down_with_growing_sigsegv_disables_pool(self): self.ping_ok = False @@ -73,6 +75,7 @@ class HealthCheckTest(unittest.TestCase): self.uci_state['pineapd.wlan2mon.hop'] = '0' self.uci_state['pineapd.wlan1mon.bands'] = '5' self.uci_state['pineapd.wlan0mon.bands'] = '2' + self.uci_state['pineapd.wlan1mon.hop'] = '0' 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])