💐 Sync 2026-10-06 12:33:44

This commit is contained in:
github-actions[bot] committed 2026-10-06 12:33:44 +08:00
1 parent 8be03ce939
commit 7fe69dbb3a
246 files changed
+50330 -3414

No files matched your search

+1 -1
View File
@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
LUCI_TITLE:=LuCI Status Pages
LUCI_DEPENDS:=+luci-base +luci-app-oui +luci-mod-dashboard +libiwinfo +rpcd-mod-iwinfo +rpcd-mod-rrdns +ucode +ucode-mod-fs +ucode-mod-ubus +ucode-mod-socket
PKG_RELEASE:=30
PKG_RELEASE:=31
PKG_BUILD_DEPENDS:=iwinfo
PKG_LICENSE:=Apache-2.0
@@ -61,8 +61,10 @@ function getResolvedIwinfoDeviceName(net, deviceLookup) {
const section = net.getName();
const device = net.getWifiDeviceName();
const fallback = getQcaFallbackIfname(device, section);
const configuredIfname = uci.get('wireless', section, 'ifname');
const isQca = isQcaWifiHwtype(uci.get('wireless', device, 'type'));
for (const candidate of [ ifname, section, fallback, device ])
for (const candidate of [ configuredIfname, ifname, section, fallback, isQca ? null : device ])
if (candidate && deviceLookup[candidate])
return candidate;
@@ -86,6 +88,9 @@ function buildIwinfoResolver(radios, networks, devices) {
}
networks.forEach((net) => {
if (isNetworkDisabled(net))
return;
const target = getResolvedIwinfoDeviceName(net, deviceLookup);
const ifname = net.getIfname();
const section = net.getName();
@@ -104,6 +109,10 @@ function buildIwinfoResolver(radios, networks, devices) {
radios.forEach((radio) => {
const name = radio.getName();
if (uci.get('wireless', name, 'disabled') == '1' ||
isQcaWifiHwtype(uci.get('wireless', name, 'type')))
return;
const target = radioTargets[name] || (deviceLookup[name] ? name : null);
registerTarget(target);
@@ -604,7 +613,21 @@ return baseclass.extend({
}),
loadIwinfoResolver(radios, networks) {
return L.resolveDefault(callIwinfoDevices(), []).then((devices) => {
// Query enabled QCA VAPs only: probing a bare wifiN may create a
// temporary VAP and block rpcd while the disabled radio starts up.
const configuredRadios = uci.sections('wireless', 'wifi-device');
const configOnly = configuredRadios.length > 0 &&
configuredRadios.every((radio) => isQcaWifiHwtype(radio.type));
const configuredDevices = [];
for (const iface of uci.sections('wireless', 'wifi-iface')) {
if (iface.disabled == '1' || uci.get('wireless', iface.device, 'disabled') == '1')
continue;
pushUnique(configuredDevices, iface.ifname || getQcaFallbackIfname(iface.device, iface['.name']));
}
return (configOnly ? Promise.resolve(configuredDevices) : L.resolveDefault(callIwinfoDevices(), [])).then((devices) => {
return buildIwinfoResolver(radios, networks, devices);
}).catch(() => ({
deviceLookup: Object.create(null),
@@ -635,6 +658,9 @@ return baseclass.extend({
},
getAssocListForNetwork(net) {
if (isNetworkDisabled(net))
return Promise.resolve([]);
const hwtype = uci.get('wireless', net.getWifiDeviceName(), 'type');
const candidates = getAssocListCandidates(net, this.iwinfoResolver);
const resolvedIfname = candidates[0];
@@ -860,7 +886,8 @@ return baseclass.extend({
}, this, radios_networks_hints[i])));
}
if (hasWPS && uci.get('wireless', radios_networks_hints[i].sid, 'wps_pushbutton') == '1') {
if (hasWPS && !isNetworkDisabled(radios_networks_hints[i]) &&
uci.get('wireless', radios_networks_hints[i].sid, 'wps_pushbutton') == '1') {
radios_networks_hints[i].isWPSEnabled = true;
tasks.push(L.resolveDefault(this.handleGetWPSStatus(radios_networks_hints[i].getIfname()), null)
.then(L.bind((net, data) => {
+65
View File
@@ -0,0 +1,65 @@
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const source = fs.readFileSync(path.join(__dirname, '../htdocs/luci-static/resources/view/status/include/60_wifi.js'), 'utf8');
let config, calls, enumerated;
const uci = {
get: (pkg, section, key) => config[section]?.[key],
sections: (pkg, type) => Object.entries(config).filter(([, s]) => s['.type'] == type).map(([name, s]) => ({'.name': name, ...s}))
};
const view = new Function('baseclass', 'rpc', 'uci', 'L', '_', source)(
{extend: o => o},
{declare: spec => async (...args) => {
calls.push([spec.method, ...args]);
return spec.method == 'devices' ? enumerated : [];
}}, uci,
{toArray: x => x || [], resolveDefault: (p, fallback) => p.catch(() => fallback)}, s => s
);
function setup() {
config = {
wifi0: {'.type':'wifi-device', type:'qcawificfg80211', disabled:'1'},
wifi1: {'.type':'wifi-device', type:'qcawifi', disabled:'1'},
ath0: {'.type':'wifi-iface', device:'wifi0'},
ath1: {'.type':'wifi-iface', device:'wifi1'}
};
calls = [];
enumerated = ['wifi0', 'wifi1'];
}
function radios() { return uci.sections('wireless', 'wifi-device').map(s => ({getName: () => s['.name']})); }
function networks() { return uci.sections('wireless', 'wifi-iface').map(s => ({
getName: () => s['.name'], getWifiDeviceName: () => s.device,
getIfname: () => s.device + '.network1', get: key => s[key],
getAssocList: async () => {calls.push(['net-assoc', s['.name']]); return [];}
})); }
(async () => {
setup();
let resolver = await view.loadIwinfoResolver(radios(), networks());
assert.deepEqual(resolver.queryTargets, []);
await view.loadIwinfoInfoMap(resolver);
for (const net of networks()) assert.deepEqual(await view.getAssocListForNetwork(net), []);
assert.deepEqual(calls, [], 'disabled radios must issue no iwinfo or association queries');
config.wifi0.disabled = '0';
resolver = await view.loadIwinfoResolver(radios(), networks());
assert.deepEqual(resolver.queryTargets, ['ath0']);
assert.equal(resolver.aliasMap.wifi0, 'ath0');
config.ath0.disabled = '1';
resolver = await view.loadIwinfoResolver(radios(), networks());
assert.deepEqual(resolver.queryTargets, [], 'disabled VAP is excluded even on an enabled radio');
config.ath0.disabled = '0';
config.ath0.ifname = 'custom0';
resolver = await view.loadIwinfoResolver(radios(), networks());
assert.deepEqual(resolver.queryTargets, ['custom0']);
delete config.ath0;
resolver = await view.loadIwinfoResolver(radios(), networks());
assert.deepEqual(resolver.queryTargets, [], 'no VAP must not fall back to the bare QCA radio');
setup();
config.radio0 = {'.type':'wifi-device', type:'mac80211'};
enumerated.push('radio0');
resolver = await view.loadIwinfoResolver(radios(), networks());
assert.deepEqual(resolver.queryTargets, ['radio0']);
assert.deepEqual(calls, [['devices']], 'mixed setups retain generic enumeration');
config.wifi0.disabled = '0';
resolver = await view.loadIwinfoResolver(radios(), networks());
assert.deepEqual(resolver.queryTargets, ['radio0'], 'mixed setups must not probe bare QCA radios');
console.log('Wi-Fi status: disabled radios/VAPs, active VAP, custom ifname, missing VAP and mixed backends passed');
})().catch(e => {console.error(e); process.exit(1);});