fix(network): async connectWifi so systemd watchdog can fire during nmcli wait

This commit is contained in:
stswangzhiping
2026-03-29 08:14:24 +08:00
parent 4c16483ee7
commit e3e9580e46
2 changed files with 41 additions and 9 deletions

View File

@@ -1,6 +1,6 @@
'use strict'; 'use strict';
const { execSync, spawnSync } = require('child_process'); const { execSync, spawnSync, spawn } = require('child_process');
const fs = require('fs'); const fs = require('fs');
const os = require('os'); const os = require('os');
const log = require('./logger'); const log = require('./logger');
@@ -222,27 +222,59 @@ function nmcliSync(args, timeoutMs = 60000) {
return (r.stdout || '').trim(); return (r.stdout || '').trim();
} }
function _delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
/** 异步 nmcli不阻塞事件循环systemd Watchdog 依赖 setInterval 在主线程运行) */
function nmcliAsync(args, timeoutMs = 60000) {
return new Promise((resolve, reject) => {
const child = spawn('nmcli', args, { stdio: ['ignore', 'pipe', 'pipe'] });
let stdout = '';
let stderr = '';
const timer = setTimeout(() => {
child.kill('SIGKILL');
reject(new Error('nmcli 超时'));
}, timeoutMs);
child.stdout.on('data', (d) => { stdout += d; });
child.stderr.on('data', (d) => { stderr += d; });
child.on('error', (err) => {
clearTimeout(timer);
reject(err);
});
child.on('close', (code) => {
clearTimeout(timer);
if (code !== 0) {
const msg = stderr.trim() || stdout.trim() || `nmcli exit ${code}`;
reject(new Error(msg));
} else {
resolve(stdout.trim());
}
});
});
}
/** /**
* 连接指定 WiFi配网场景成功 = NM 显示 STA 已连上目标网,不要求一定能 ping 通 8.8.8.8 * 连接指定 WiFi配网场景成功 = NM 显示 STA 已连上目标网,不要求一定能 ping 通 8.8.8.8
* @returns {{ success: boolean, error?: string }} * 必须异步:同步 spawnSync + execSync(sleep) 会卡住主线程,导致 systemd WatchdogSec 内收不到 WATCHDOG=1。
* @returns {Promise<{ success: boolean, error?: string }>}
*/ */
function connectWifi(ssid, password) { async function connectWifi(ssid, password) {
const iface = getWifiIface(); const iface = getWifiIface();
log.info('network', `尝试连接 WiFi: ${ssid}ifname=${iface}`); log.info('network', `尝试连接 WiFi: ${ssid}ifname=${iface}`);
try { try {
try { try {
nmcliSync(['connection', 'delete', ssid], 15000); await nmcliAsync(['connection', 'delete', ssid], 15000);
} catch (_) {} } catch (_) {}
// 关热点后部分固件需显式保证由 NM 管理、再关联
try { try {
nmcliSync(['device', 'set', iface, 'managed', 'yes'], 8000); await nmcliAsync(['device', 'set', iface, 'managed', 'yes'], 8000);
} catch (_) {} } catch (_) {}
const args = ['device', 'wifi', 'connect', ssid]; const args = ['device', 'wifi', 'connect', ssid];
if (password) args.push('password', password); if (password) args.push('password', password);
args.push('ifname', iface); args.push('ifname', iface);
nmcliSync(args, 120000); await nmcliAsync(args, 120000);
const deadline = Date.now() + CONNECT_WIFI_STA_WAIT_MS; const deadline = Date.now() + CONNECT_WIFI_STA_WAIT_MS;
while (Date.now() < deadline) { while (Date.now() < deadline) {
@@ -257,7 +289,7 @@ function connectWifi(ssid, password) {
} }
return { success: true }; return { success: true };
} }
sleep(CONNECT_WIFI_STA_POLL_MS); await _delay(CONNECT_WIFI_STA_POLL_MS);
} }
return { success: false, error: '超时:网卡未进入已连接状态' }; return { success: false, error: '超时:网卡未进入已连接状态' };
} catch (e) { } catch (e) {

View File

@@ -168,7 +168,7 @@ class ProvisionManager extends EventEmitter {
// 关热点后射频/模式切换需要时间,立刻 connect 在部分板子上会失败 // 关热点后射频/模式切换需要时间,立刻 connect 在部分板子上会失败
await new Promise((r) => setTimeout(r, 3500)); await new Promise((r) => setTimeout(r, 3500));
const result = connectWifi(ssid, password); const result = await connectWifi(ssid, password);
if (result.success) { if (result.success) {
this._state = 'sta'; this._state = 'sta';