feat(svc_perf): network 采集并入 perf 聚合 — 真实 WiFi(wpa_cli/ip) + 蓝牙(hci0/sysfs) + hostname_local, SYSTEM 子集输出, /api/perf 保持轻量 (agent-165)

This commit is contained in:
MeowOS committed 2026-08-31 01:06:30 +08:00
1 parent c9a1d0e6af
commit 365cf0f1c0
1 file changed
+125
+125
View File
@@ -312,6 +312,129 @@ static void sys_kernel(char *out, int cap)
out[rl] = 0;
}
/* ---------------- 网络(wifi/bluetooth/hostname_local,SYSTEM 子集) ----------------
* 单一权威源:/api/system、/api/status 的 network 段由 meow_svc_perf 聚合(SYSTEM_FRAMEWORK_SERVICES §4.1)。
* 采集只走 popen(wpa_cli/ip/hciconfig) + /sys 只读,带超时防挂;拿不到的真实值回 null/占位,绝不编造。
*/
/* wifi:解析 wpa_cli status;返回 connected(1/0)。ssid/ip 失败置空串(→ null)。 */
static int net_wifi(char *ssid, size_t sscap, char *ip, size_t ipcap)
{
ssid[0] = 0; ip[0] = 0;
int connected = 0;
char *o = popen_read_tmo(MEOW_TIMEOUT_MS,
"PATH=/bin:/sbin:/usr/bin:/usr/sbin wpa_cli status 2>/dev/null");
if (o) {
char *ln = o;
while (ln && *ln) {
char *nl = strchr(ln, '\n');
if (nl) *nl = 0;
if (!strncmp(ln, "wpa_state=", 10)) { if (!strcmp(ln + 10, "COMPLETED")) connected = 1; }
else if (!strncmp(ln, "ssid=", 5)) { snprintf(ssid, sscap, "%s", ln + 5); }
if (!nl) break;
ln = nl + 1;
}
free(o);
}
/* IP:ip -o -4 addr show wlan0 → "2: wlan0 inet 10.78.193.50/24 ...",取含 '.'+'/' 的 token 的 '/' 前段 */
char *o2 = popen_read_tmo(MEOW_TIMEOUT_MS,
"PATH=/bin:/sbin:/usr/bin:/usr/sbin ip -o -4 addr show wlan0 2>/dev/null");
if (o2) {
for (char *tk = strtok(o2, " \t"); tk; tk = strtok(NULL, " \t")) {
char *dot = strchr(tk, '.'); if (!dot) continue;
char *sl = strchr(tk, '/'); if (!sl || sl < dot) continue;
size_t len = (size_t)(sl - tk);
if (len >= ipcap) len = ipcap - 1;
memcpy(ip, tk, len); ip[len] = 0;
break;
}
free(o2);
}
return connected;
}
/* bluetooth 段字段(不含外层 {}):available/powered/address/connected/devices。 */
static void net_bluetooth_json(sbuf_t *s)
{
int available = 0;
struct stat st;
if (stat("/sys/class/bluetooth/hci0", &st) == 0) available = 1;
sA(s, "\"available\":%s", available ? "true" : "false");
/* address:/sys/class/bluetooth/hci0/address 优先,回退 hciconfig 的 BD Address */
char addr[32] = "";
if (available) {
read_file_txt("/sys/class/bluetooth/hci0/address", addr, sizeof addr);
if (!addr[0]) {
char *o = popen_read_tmo(MEOW_TIMEOUT_MS,
"PATH=/bin:/sbin:/usr/bin:/usr/sbin hciconfig hci0 2>/dev/null");
if (o) {
char *p = strstr(o, "BD Address:");
if (p) {
p += 11; /* "BD Address:" 长 11 */
while (*p == ' ') p++;
int i = 0;
while (*p && *p != ' ' && *p != '\n' && i < (int)sizeof addr - 1) addr[i++] = *p++;
addr[i] = 0;
}
free(o);
}
}
}
sA(s, ",\"address\":");
if (addr[0]) sA_str(s, addr); else sA(s, "null");
/* powered:/sys/.../power(on/off)优先,文件不可读回退 hciconfig 行首 UP/DOWN */
int powered = 0;
char pw[8] = "";
int pwok = 0;
if (available) {
pwok = read_file_txt("/sys/class/bluetooth/hci0/power", pw, sizeof pw);
if (pwok > 0 && pw[0]) {
powered = (!strcmp(pw, "on"));
} else {
char *o = popen_read_tmo(MEOW_TIMEOUT_MS,
"PATH=/bin:/sbin:/usr/bin:/usr/sbin hciconfig hci0 2>/dev/null");
if (o) {
char *ln = o;
while (ln && *ln) {
char *nl = strchr(ln, '\n');
if (nl) *nl = 0;
char *q = ln;
while (*q == ' ' || *q == '\t') q++;
if (!strncmp(q, "UP", 2)) { powered = 1; break; }
if (!strncmp(q, "DOWN", 4)) { powered = 0; break; }
if (!nl) break;
ln = nl + 1;
}
free(o);
}
}
}
sA(s, ",\"powered\":%s", powered ? "true" : "false");
/* connected/devices:无配对记录可查(/var/lib/bluetooth 空/未知)→ false/[],不编造 */
sA(s, ",\"connected\":false,\"devices\":[]");
}
/* network 完整对象(含外层 {})。hostname 无效(空/"-")时 hostname_local=null。 */
static void net_json(sbuf_t *s, const char *hostname)
{
char ssid[64] = "", ip[64] = "";
int wc = net_wifi(ssid, sizeof ssid, ip, sizeof ip);
sA(s, "{\"wifi\":{\"connected\":%s", wc ? "true" : "false");
sA(s, ",\"ssid\":");
if (ssid[0]) sA_str(s, ssid); else sA(s, "null");
sA(s, ",\"ip\":");
if (ip[0]) sA_str(s, ip); else sA(s, "null");
sA(s, ",\"rssi\":null,\"bitrate_mbps\":null},\"bluetooth\":{");
net_bluetooth_json(s);
sA(s, "},\"hostname_local\":");
const char *hn = (hostname && hostname[0] && strcmp(hostname, "-")) ? hostname : NULL;
if (!hn) { sA(s, "null"); return; }
char hl[96];
size_t l = strlen(hn);
if (l >= 6 && !strcmp(hn + l - 6, ".local")) snprintf(hl, sizeof hl, "%s", hn);
else snprintf(hl, sizeof hl, "%s.local", hn);
sA_str(s, hl);
}
/* ---------------- 聚合 JSON(subset 位掩码;与 MEOWPANEL_PROTOCOL §3.1 .data 对齐) ---------------- */
static int agg_json(int subset, char *out, int cap)
{
@@ -369,6 +492,8 @@ static int agg_json(int subset, char *out, int cap)
sA(&s, ",\"kernel\":"); sA_str(&s, ker);
sA(&s, ",\"arch\":"); sA_str(&s, arch);
sA(&s, "}");
/* network(wifi/bluetooth/hostname_local):SYSTEM 子集,/api/system 与 /api/status 透传 */
SEP(); sA(&s, "\"network\":"); net_json(&s, host);
}
sA(&s, "}"); /* 外层闭合 */
#undef SEP