refactor(network): unify sysfs carrier reads and LAN vs wired iface logic
This commit is contained in:
118
lib/network.js
118
lib/network.js
@@ -1,17 +1,41 @@
|
||||
'use strict';
|
||||
|
||||
const { execSync } = require('child_process');
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const log = require('./logger');
|
||||
|
||||
const AP_SSID_PREFIX = 'ClawBox-';
|
||||
const AP_IP = '10.42.0.1';
|
||||
const AP_PASSWORD = '12345678';
|
||||
const AP_IFACE = process.env.CLAWD_WIFI_IFACE || '';
|
||||
const CON_NAME = 'clawd-hotspot';
|
||||
const AP_IP = '10.42.0.1';
|
||||
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;
|
||||
}
|
||||
} catch (_) {}
|
||||
if (_sysfsCarrierUp(name)) return name;
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 物理链路是否 up:carrier + 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();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user