fix: health monitor detects down monitors via ip link flags + disables wlan2mon
operstate reports 'unknown' on monitors (normal), so _iface_up now parses admin flags from ip link. Discovered a second pineapd SIGSEGV source: the wlan2mon 6GHz monitor this hardware never creates, hopping on the missing iface (~85s crash cadence even with the SSID pool off). The monitor now disables it in the fix path.
This commit is contained in:
@@ -3341,9 +3341,20 @@ def _sigsegv_count():
|
||||
return out.count('SIGSEGV')
|
||||
|
||||
|
||||
def _iface_up(name):
|
||||
"""True when the interface's admin flags contain UP. Monitor interfaces
|
||||
report operstate 'unknown' even when usable, so parse `ip link` flags."""
|
||||
rc, out, err = device_run(['ip', 'link', 'show', name], timeout=10)
|
||||
if rc != 0:
|
||||
return False
|
||||
m = re.search(r'<([^>]+)>', out or '')
|
||||
if not m:
|
||||
return False
|
||||
return 'UP' in m.group(1).split(',')
|
||||
|
||||
|
||||
def _monitor_down(name):
|
||||
rc, out, err = device_run(['iw', 'dev'], timeout=10)
|
||||
return name not in out
|
||||
return not _iface_up(name)
|
||||
|
||||
|
||||
def health_check():
|
||||
@@ -3366,11 +3377,23 @@ def health_check():
|
||||
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'):
|
||||
device_run(['ip', 'link', 'set', 'wlan1mon', 'up'], timeout=10)
|
||||
h['last_action'] = 'wlan1mon brought up'
|
||||
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'] = 'pineapd restart'
|
||||
# wlan2mon is a 6GHz monitor this hardware never materializes; with
|
||||
# hop enabled pineapd crashes on the missing iface (second SIGSEGV
|
||||
# source after the SSID pool). Keep it disabled.
|
||||
wlan2 = _uci_section('pineapd.wlan2mon')
|
||||
if wlan2.get('disable') != '1':
|
||||
device_run(['uci', 'set', 'pineapd.wlan2mon.disable=1'])
|
||||
device_run(['uci', 'set', 'pineapd.wlan2mon.hop=0'])
|
||||
device_run(['uci', 'commit', 'pineapd'])
|
||||
h['last_action'] = 'wlan2mon disabled (missing 6GHz iface crash source)'
|
||||
else:
|
||||
h['last_action'] = 'pineapd restart'
|
||||
device_run(['/etc/init.d/pineapd', 'restart'], timeout=30)
|
||||
h['sigsegv_last'] = _sigsegv_count()
|
||||
h['last_fix'] = now
|
||||
|
||||
@@ -15,11 +15,13 @@ class HealthCheckTest(unittest.TestCase):
|
||||
self.runs = []
|
||||
self.ping_ok = True
|
||||
self.sigsegvs = 0
|
||||
self.iw_out = 'wlan0mon\nwlan1mon\n'
|
||||
self.iface_up = {'wlan0mon': True, 'wlan1mon': True}
|
||||
self.uci_state = {}
|
||||
server._health.update({
|
||||
'sigsegv_last': None, 'last_fix': 0.0, 'fixes': 0,
|
||||
'last_action': None, 'pineap_up': False})
|
||||
self.old_iface_up = server._iface_up
|
||||
server._iface_up = lambda name: self.iface_up.get(name, True)
|
||||
|
||||
def fake_run(args, timeout=20, input_data=None):
|
||||
self.runs.append((list(args), timeout))
|
||||
@@ -30,8 +32,6 @@ class HealthCheckTest(unittest.TestCase):
|
||||
return (1, '', 'could not connect to pineap: connection refused')
|
||||
if a[0] == 'logread':
|
||||
return (0, 'SIGSEGV\n' * self.sigsegs if hasattr(self, 'sigsegs') else '', '')
|
||||
if a[0] == 'iw':
|
||||
return (0, self.iw_out, '')
|
||||
if a[:2] == ['uci', 'set']:
|
||||
k, _, v = a[2].partition('=')
|
||||
self.uci_state[k] = v
|
||||
@@ -45,6 +45,9 @@ class HealthCheckTest(unittest.TestCase):
|
||||
|
||||
server.device_run = fake_run
|
||||
|
||||
def tearDown(self):
|
||||
server._iface_up = self.old_iface_up
|
||||
|
||||
def test_pineap_up_reports_no_action(self):
|
||||
result = server.health_check()
|
||||
self.assertTrue(result['pineap_up'])
|
||||
@@ -66,12 +69,12 @@ class HealthCheckTest(unittest.TestCase):
|
||||
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_monitors_up(self):
|
||||
self.ping_ok = False
|
||||
self.uci_state['pineapd.@ssidpool[0].disable'] = '1'
|
||||
self.iw_out = 'wlan0mon\n'
|
||||
self.iface_up = {'wlan0mon': True, 'wlan1mon': False}
|
||||
result = server.health_check()
|
||||
self.assertEqual(result['last_action'], 'wlan1mon brought up')
|
||||
self.assertEqual(result['last_action'], 'monitor interfaces brought up')
|
||||
self.assertIn(['ip', 'link', 'set', 'wlan1mon', 'up'], [r[0] for r in self.runs])
|
||||
|
||||
def test_down_disables_pool_regardless_of_sigsegv_history(self):
|
||||
|
||||
Reference in New Issue
Block a user