refactor(network): unify sysfs carrier reads and LAN vs wired iface logic

This commit is contained in:
stswangzhiping
2026-03-28 22:33:29 +08:00
parent 1910a2fb9f
commit ea502fc26a

View File

@@ -11,7 +11,31 @@ const AP_PASSWORD = '12345678';
const AP_IFACE = process.env.CLAWD_WIFI_IFACE || '';
const CON_NAME = 'clawd-hotspot';
/** 非 WiFi、非典型虚拟接口用于自动发现有线口如 end0、enp*,而非仅 eth0 */
/** 产品 RJ45 在 sysfs 中的默认名;等价于检测 `cat /sys/class/net/end0/carrier` */
const DEFAULT_ETH_IFACE = 'end0';
function _ethIfaceEnvOrDefault() {
return process.env.CLAWD_ETH_IFACE || DEFAULT_ETH_IFACE;
}
function _netIfaceExists(name) {
try {
return fs.existsSync(`/sys/class/net/${name}`);
} catch (_) {
return false;
}
}
/** 读取 `/sys/class/net/<iface>/carrier``1` 为链路 up缺失或异常视为 down */
function _sysfsCarrierUp(iface) {
try {
return fs.readFileSync(`/sys/class/net/${iface}/carrier`, 'utf8').trim() === '1';
} catch (_) {
return false;
}
}
/** 非 WiFi、非典型虚拟接口用于开发机扫描有线口enp* 等) */
function _isExcludedVirtualIface(name) {
if (name === 'lo' || name === 'bonding_masters') return true;
if (name.startsWith('wl')) return true;
@@ -26,85 +50,45 @@ function _isExcludedVirtualIface(name) {
}
/**
* 返回当前链路 up 的有线网卡名
* 若设置 CLAWD_ETH_IFACE只认该接口否则扫描 sysfs与仅默认 eth0 相比适配更多板型)。
* 开发机:无 CLAWD_ETH_IFACE 且无 end0 时,扫描 sysfs 找第一个 carrier=1 的有线
*/
function getWiredIfaceWithCarrier() {
const explicit = process.env.CLAWD_ETH_IFACE;
if (explicit) {
try {
if (fs.readFileSync(`/sys/class/net/${explicit}/carrier`, 'utf8').trim() === '1') {
return explicit;
}
} catch (_) {}
return null;
}
function _firstScanWiredIfaceWithCarrier() {
try {
const names = fs.readdirSync('/sys/class/net');
for (const name of names.sort()) {
if (_isExcludedVirtualIface(name)) continue;
try {
if (fs.readFileSync(`/sys/class/net/${name}/carrier`, 'utf8').trim() === '1') {
return name;
if (_sysfsCarrierUp(name)) return name;
}
} catch (_) {}
}
} catch (_) {}
return null;
}
/**
* 是否存在任一带 carrier 的有线接口(无延迟)
* 返回当前可用于「有线 ping / 路由」的网卡名。
* 优先级CLAWD_ETH_IFACE → 存在 end0 则只用 end0 → 否则扫描 sysfs。
*/
function getWiredIfaceWithCarrier() {
const explicit = process.env.CLAWD_ETH_IFACE;
if (explicit) {
return _netIfaceExists(explicit) && _sysfsCarrierUp(explicit) ? explicit : null;
}
if (_netIfaceExists(DEFAULT_ETH_IFACE)) {
return _sysfsCarrierUp(DEFAULT_ETH_IFACE) ? DEFAULT_ETH_IFACE : null;
}
return _firstScanWiredIfaceWithCarrier();
}
function hasWiredCarrier() {
return getWiredIfaceWithCarrier() !== null;
}
/**
* 物理链路是否 upcarrier + operstate 双读,避免单读 carrier 拔线不更新、或与 WiFi 状态不同步的误判
*/
function _ifacePhysicalLinkUp(iface) {
try {
const base = `/sys/class/net/${iface}`;
const carrier = fs.readFileSync(`${base}/carrier`, 'utf8').trim();
if (carrier !== '1') return false;
let oper = 'unknown';
try {
oper = fs.readFileSync(`${base}/operstate`, 'utf8').trim();
} catch (_) {}
if (oper === 'down' || oper === 'lowerlayerdown') return false;
return true;
} catch (_) {
return false;
}
}
/**
* LAN 面板灯:只反映指定/板载以太网 RJ45 链路,与 WiFi 无关。
* - CLAWD_ETH_IFACE只读该口
* - 否则只认 end0、eth0存在则仅用其 sysfs链路 down 即灭,不再退回「任一带 carrier 口」以免跟 WiFi 耦合)
* - 无 end0/eth0 节点时(开发机)退回 hasWiredCarrier()
* LAN 面板灯:只反映 RJ45 对应口,与 `cat /sys/class/net/end0/carrier 2>/dev/null` 同源(仅读 carrier
* 若配置的接口在 sysfs 中不存在(常见为开发机无 end0则退回与 hasWiredCarrier() 一致,避免灯永远灭。
*/
function hasLanCableCarrier() {
const explicit = process.env.CLAWD_ETH_IFACE;
if (explicit) {
try {
return fs.existsSync(`/sys/class/net/${explicit}`) && _ifacePhysicalLinkUp(explicit);
} catch (_) {
return false;
}
}
let hasBoardIface = false;
for (const n of ['end0', 'eth0']) {
if (!fs.existsSync(`/sys/class/net/${n}`)) continue;
hasBoardIface = true;
if (_ifacePhysicalLinkUp(n)) return true;
}
if (hasBoardIface) return false;
const iface = _ethIfaceEnvOrDefault();
if (_netIfaceExists(iface)) return _sysfsCarrierUp(iface);
return hasWiredCarrier();
}