fix: passive health check + hop=0 in stabilization pass
Active PINGs on pineapd's command socket collided with the stock daemon's
own socket writes ('[PineAp] Error writing'), making the daemon watchdog
SIGTERM pineapd every ~30s while hopping. Monitor now checks pidof only
(no socket writes) and the stabilization pass pins wlan1mon hop=0.
This commit is contained in:
@@ -3347,10 +3347,11 @@ def _stabilize_pineapd():
|
|||||||
1. SSID-pool broadcast (segfault, ~15s cadence)
|
1. SSID-pool broadcast (segfault, ~15s cadence)
|
||||||
2. wlan2mon: a 6GHz monitor this hardware never creates; hopping the
|
2. wlan2mon: a 6GHz monitor this hardware never creates; hopping the
|
||||||
missing iface segfaults pineapd
|
missing iface segfaults pineapd
|
||||||
3. wlan1mon fast-hopping the 6GHz channel list (ASIO io thread exit)
|
3. wlan1mon fast-hopping (stalls the command socket; the stock daemon's
|
||||||
The pool list itself is also cleared: collect refills it, and a large
|
watchdog then SIGTERMs pineapd every ~30s)
|
||||||
pool was observed crashing even with broadcast disabled.
|
4. a large refilled pool (collect refills it; crashes observed even
|
||||||
Returns a human-readable description of what was changed."""
|
with broadcast disabled)
|
||||||
|
The pool list itself is cleared. Returns what changed."""
|
||||||
actions = []
|
actions = []
|
||||||
wanted = {
|
wanted = {
|
||||||
'pineapd.@ssidpool[0].disable': '1',
|
'pineapd.@ssidpool[0].disable': '1',
|
||||||
@@ -3358,6 +3359,7 @@ def _stabilize_pineapd():
|
|||||||
'pineapd.wlan2mon.hop': '0',
|
'pineapd.wlan2mon.hop': '0',
|
||||||
'pineapd.wlan1mon.bands': '5',
|
'pineapd.wlan1mon.bands': '5',
|
||||||
'pineapd.wlan0mon.bands': '2',
|
'pineapd.wlan0mon.bands': '2',
|
||||||
|
'pineapd.wlan1mon.hop': '0',
|
||||||
}
|
}
|
||||||
for key, value in wanted.items():
|
for key, value in wanted.items():
|
||||||
rc, out, err = device_run(['uci', 'get', key])
|
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
|
"""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
|
The check is PASSIVE (pidof) — actively pinging pineapd's command
|
||||||
pineapd is unreachable the pool broadcast is disabled before restarting,
|
socket every 15s collides with the stock daemon's own socket writes
|
||||||
since it is the only known crash cause."""
|
('[PineAp] Error writing' -> daemon watchdog SIGTERMs pineapd).
|
||||||
|
"""
|
||||||
h = _health
|
h = _health
|
||||||
rc, out, err = device_run([HAK5CMD, 'PING'], timeout=10)
|
rc, out, err = device_run(['pidof', 'pineapd'], timeout=10)
|
||||||
h['pineap_up'] = rc == 0 and 'PONG' in (out or '')
|
h['pineap_up'] = rc == 0 and bool((out or '').strip())
|
||||||
if h['pineap_up']:
|
if h['pineap_up']:
|
||||||
return dict(h)
|
return dict(h)
|
||||||
now = time.time()
|
now = time.time()
|
||||||
|
|||||||
@@ -26,10 +26,10 @@ class HealthCheckTest(unittest.TestCase):
|
|||||||
def fake_run(args, timeout=20, input_data=None):
|
def fake_run(args, timeout=20, input_data=None):
|
||||||
self.runs.append((list(args), timeout))
|
self.runs.append((list(args), timeout))
|
||||||
a = list(args)
|
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:
|
if self.ping_ok:
|
||||||
return (0, 'Sending PING...\nGot PONG response\n', '')
|
return (0, '12345\n', '')
|
||||||
return (1, '', 'could not connect to pineap: connection refused')
|
return (1, '', '')
|
||||||
if a[0] == 'logread':
|
if a[0] == 'logread':
|
||||||
return (0, 'SIGSEGV\n' * self.sigsegs if hasattr(self, 'sigsegs') else '', '')
|
return (0, 'SIGSEGV\n' * self.sigsegs if hasattr(self, 'sigsegs') else '', '')
|
||||||
if a[:2] == ['uci', 'set']:
|
if a[:2] == ['uci', 'set']:
|
||||||
@@ -56,6 +56,8 @@ class HealthCheckTest(unittest.TestCase):
|
|||||||
result = server.health_check()
|
result = server.health_check()
|
||||||
self.assertTrue(result['pineap_up'])
|
self.assertTrue(result['pineap_up'])
|
||||||
self.assertIsNone(result['last_action'])
|
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):
|
def test_down_with_growing_sigsegv_disables_pool(self):
|
||||||
self.ping_ok = False
|
self.ping_ok = False
|
||||||
@@ -73,6 +75,7 @@ class HealthCheckTest(unittest.TestCase):
|
|||||||
self.uci_state['pineapd.wlan2mon.hop'] = '0'
|
self.uci_state['pineapd.wlan2mon.hop'] = '0'
|
||||||
self.uci_state['pineapd.wlan1mon.bands'] = '5'
|
self.uci_state['pineapd.wlan1mon.bands'] = '5'
|
||||||
self.uci_state['pineapd.wlan0mon.bands'] = '2'
|
self.uci_state['pineapd.wlan0mon.bands'] = '2'
|
||||||
|
self.uci_state['pineapd.wlan1mon.hop'] = '0'
|
||||||
result = server.health_check()
|
result = server.health_check()
|
||||||
self.assertEqual(result['last_action'], 'pineapd restart')
|
self.assertEqual(result['last_action'], 'pineapd restart')
|
||||||
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])
|
||||||
|
|||||||
Reference in New Issue
Block a user