/* netmgr_ipc.c — MeowOS launcher → meow_netmgrd IPC 客户端(M6.0 设置页迁移,NETMGR_DESIGN §12)。 * * 【迁移注释】wifi_page/bt_page 的 WiFi/BT 查询与操作统一走 netmgrd IPC(socket * /usr/data/meow_net.sock,env MEOW_NET_SOCK 覆盖)。netmgrd 是 WiFi/BT 操作权威 * (IPC 0xE4 WIFI_CTL / 0xE6 BT_CTL);本文件是「设置页客户端 → 权威 IPC」的唯一入口。 * 【双轨过渡】netmgrd 不可达(NETMGR_UNREACH)时,本文件回退到 net.c 既有的直连 * wpa_cli/hciconfig/busybox 读取(保功能);状态栏物理键语义、sdk/amsd/netmgrd 本体不动。 * * 鉴权:跟 pm_power_ipc 同款 —— 先 MSG_HELLO(app_id=os.meow.launcher,netmgrd 按 * SO_PEERCRED 认出 /usr/bin/meow_launcherd = 系统身份,写级放行),再发 CTL,收 RESULT。 * 只读观测(wlan_connected()/hci_powered() 读 /sys 链路态)仍在 net.c;此处只接权威操作。 */ #include "meow_launcher_internal.h" #define NETMGR_SOCK_DEFAULT "/usr/data/meow_net.sock" /* NETMGR_UNREACH 定义在 meow_launcher_internal.h(测试/调用方共用) */ /* 可达性缓存:netmgrd 不可达时每次 connect 都秒回 ECONNREFUSED,无成本;缓存约 1s 防热循环。 */ static int g_nm_ok = -1; /* -1 未知 / 0 down / 1 up */ static time_t g_nm_ok_ts = 0; static void nm_cache(int ok) { g_nm_ok = ok; g_nm_ok_ts = time(NULL); } /* ---- socket 连接 ---- */ static int nm_sock(void) { const char *sock = getenv("MEOW_NET_SOCK"); if (!sock || !sock[0]) sock = NETMGR_SOCK_DEFAULT; int fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) return -1; struct sockaddr_un a; memset(&a, 0, sizeof a); a.sun_family = AF_UNIX; strncpy(a.sun_path, sock, sizeof a.sun_path - 1); if (connect(fd, (struct sockaddr *)&a, sizeof a) < 0) { close(fd); return -1; } return fd; } /* ---- 帧收发(复刻 power_menu.c pw_xchg:读超时 1.5s,绝不让子页主循环卡死)---- */ static int nm_w_all(int fd, const uint8_t *b, size_t n) { size_t off = 0; while (off < n) { ssize_t w = write(fd, b + off, n - off); if (w <= 0) return -1; off += (size_t)w; } return 0; } static int nm_r_all(int fd, uint8_t *b, size_t n) { size_t off = 0; while (off < n) { ssize_t r = read(fd, b + off, n - off); if (r <= 0) return -1; off += (size_t)r; } return 0; } static int nm_xchg(int fd, uint16_t type, uint32_t req, const void *pl, uint32_t len, uint8_t *rbuf, uint32_t rcap, uint16_t *rtype) { uint8_t hdr[MEOW_IPC_HDR_LEN]; uint32_t magic = MEOW_IPC_MAGIC, rlen = len; uint16_t ver = MEOW_IPC_VERSION, t = type; memcpy(hdr + 0, &magic, 4); memcpy(hdr + 4, &ver, 2); memcpy(hdr + 6, &t, 2); memcpy(hdr + 8, &req, 4); memcpy(hdr + 12, &rlen, 4); if (nm_w_all(fd, hdr, sizeof hdr) < 0) return -1; if (len && pl && nm_w_all(fd, pl, len) < 0) return -1; struct pollfd pf = { fd, POLLIN, 0 }; int pr = poll(&pf, 1, 1500); if (pr <= 0) return -1; uint8_t rhdr[MEOW_IPC_HDR_LEN]; if (nm_r_all(fd, rhdr, sizeof rhdr) < 0) return -1; uint32_t rmagic, rlen2; uint16_t rver, rt; memcpy(&rmagic, rhdr, 4); memcpy(&rver, rhdr + 4, 2); memcpy(&rt, rhdr + 6, 2); memcpy(&rlen2, rhdr + 12, 4); if (rmagic != MEOW_IPC_MAGIC || rver != MEOW_IPC_VERSION) return -1; if (rlen2 > rcap) rlen2 = rcap; if (rlen2 && nm_r_all(fd, rbuf, rlen2) < 0) return -1; if (rtype) *rtype = rt; return 0; } static int nm_hello(int fd) { m_hello_t h; memset(&h, 0, sizeof h); snprintf(h.app_id, sizeof h.app_id, "%s", "os.meow.launcher"); h.version = 1; uint8_t rbuf[256]; uint16_t rtype = 0; if (nm_xchg(fd, MSG_HELLO, 0x100, &h, sizeof h, rbuf, sizeof rbuf, &rtype) < 0 || rtype != MSG_ACK) return -1; m_ack_t ak; memcpy(&ak, rbuf, sizeof ak); return (ak.code == MEOW_OK) ? 0 : -1; } /* 发一条 WIFI_CTL/BT_CTL 子命令,收 RESULT。返回 netmgrd 业务 code;NETMGR_UNREACH=不可达。 */ static int nm_cmd(uint16_t ctl_type, uint16_t res_type, const char *cmd, const char *arg, char *text, int tlen) { m_net_cmd_t q; memset(&q, 0, sizeof q); snprintf(q.cmd, sizeof q.cmd, "%s", cmd ? cmd : ""); if (arg) snprintf(q.arg, sizeof q.arg, "%s", arg); int fd = nm_sock(); if (fd < 0) { nm_cache(0); return NETMGR_UNREACH; } if (nm_hello(fd) < 0) { close(fd); nm_cache(0); return NETMGR_UNREACH; } uint8_t rbuf[MEOW_IPC_MAX]; uint16_t rtype = 0; memset(rbuf, 0, sizeof rbuf); if (nm_xchg(fd, ctl_type, 0x101, &q, sizeof q, rbuf, sizeof rbuf, &rtype) < 0 || rtype != res_type) { close(fd); nm_cache(0); return NETMGR_UNREACH; } m_net_cmd_result_t res; memcpy(&res, rbuf, sizeof res); close(fd); nm_cache(1); uint32_t tl = res.text_len; if (tl >= (uint32_t)tlen) tl = (uint32_t)(tlen - 1); if (text && tlen > 0) { memcpy(text, res.text, tl); text[tl] = 0; } return (int)res.code; } int netmgr_wifi(const char *cmd, const char *arg, char *text, int tlen) { return nm_cmd(MSG_NET_WIFI_CTL, MSG_NET_WIFI_RESULT, cmd, arg, text, tlen); } int netmgr_bt(const char *cmd, const char *arg, char *text, int tlen) { return nm_cmd(MSG_NET_BT_CTL, MSG_NET_BT_RESULT, cmd, arg, text, tlen); } int netmgr_ok(void) { if (g_nm_ok >= 0 && time(NULL) - g_nm_ok_ts < 1) return g_nm_ok; int fd = nm_sock(); if (fd < 0) { nm_cache(0); return 0; } close(fd); nm_cache(1); return 1; } /* ================= WiFi(IPC 优先,net.c 直连回退)================= */ /* —— net.c 的 wpa_decode_ssid 是 static,这里复制最小实现(本模块自含,避免改 net.c 可见性)—— */ static int nm_hex(char c) { if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return c - 'a' + 10; if (c >= 'A' && c <= 'F') return c - 'A' + 10; return -1; } static void nm_decode_ssid(const char *src, char *dst, int dlen) { int o = 0; while (*src && o < dlen - 1) { if (src[0] == '\\' && src[1] == 'x' && nm_hex(src[2]) >= 0 && nm_hex(src[3]) >= 0) { dst[o++] = (char)((nm_hex(src[2]) << 4) | nm_hex(src[3])); src += 4; } else { dst[o++] = *src++; } } dst[o] = 0; } /* —— netmgrd `wifi status` 单行摘要解析:`wifi state=X connected=N ssid=S ip=I rssi=R`。 * 已知字段以「空格+key=」分隔;ssid 可能含空格,需读到下一个已知字段为止。 —— */ static int nm_is_status_key(const char *p) { if (!strncmp(p, " ip=", 4) || !strncmp(p, " rssi=", 6) || !strncmp(p, " connected=", 11) || !strncmp(p, " state=", 7)) return 1; return 0; } static const char *nm_val(const char *text, const char *key, char *dst, int dlen, int space_ok) { const char *p = strstr(text, key); if (!p) { dst[0] = 0; return p; } p += strlen(key); int o = 0; while (*p && o < dlen - 1) { if (*p == ' ') { if (!space_ok) break; if (nm_is_status_key(p)) break; /* ssid 含空格:停在下一个已知字段 */ } dst[o++] = *p++; } dst[o] = 0; return p; } static int nm_parse_connected(const char *text) { char v[8]; nm_val(text, "connected=", v, sizeof v, 0); return atoi(v); } int nm_wifi_connected(void) { char text[256]; int rc = netmgr_wifi("status", NULL, text, sizeof text); if (rc == NETMGR_UNREACH) return wlan_connected(); if (rc != MEOW_OK) return 0; return nm_parse_connected(text); } void nm_wifi_ssid(char *buf, int len) { buf[0] = 0; char text[256]; int rc = netmgr_wifi("status", NULL, text, sizeof text); if (rc == NETMGR_UNREACH) { net_wifi_ssid(buf, len); return; } if (rc != MEOW_OK) return; if (!nm_parse_connected(text)) return; char raw[128]; nm_val(text, "ssid=", raw, sizeof raw, 1); nm_decode_ssid(raw, buf, len); } int nm_wifi_info(char (*info)[48], int max) { char text[256]; int rc = netmgr_wifi("status", NULL, text, sizeof text); if (rc == NETMGR_UNREACH) return net_wifi_info(info, max); /* 回退:完整详情(直连 wpa_cli+ip route) */ if (rc != MEOW_OK || !nm_parse_connected(text)) return 0; char ssid[40] = "", ip[32] = "", rssi[16] = ""; char raw[128]; nm_val(text, "ssid=", raw, sizeof raw, 1); nm_decode_ssid(raw, ssid, sizeof ssid); nm_val(text, "ip=", ip, sizeof ip, 0); nm_val(text, "rssi=", rssi, sizeof rssi, 0); int n = 0; if (ssid[0]) snprintf(info[n++], 48, "SSID: %s", ssid); if (ip[0]) snprintf(info[n++], 48, "本机IP: %s", ip); if (rssi[0]) snprintf(info[n++], 48, "信号: %s dBm", rssi); if (n == 0) snprintf(info[n++], 48, "已连接(无详情)"); return n; } /* —— 解析 netmgrd `wifi networks`(wpa_cli list_networks 原样文本)→ meow_net_t[]; * 直接复用 net.c 既有表头跳行逻辑。返回项数。 —— */ static int nm_parse_saved(const char *txt, meow_net_t *out, int max) { int n = 0; if (!txt || max <= 0) return 0; if (max > S_NET_MAX) max = S_NET_MAX; const char *line = strchr(txt, '\n'); if (line) line++; while (line && *line && n < max) { const char *eol = strchr(line, '\n'); const char *p = line; while (*p == ' ' || *p == '\t') p++; if (*p && *p != '#') { int id = atoi(p); const char *sp = p; while (*sp && *sp != ' ' && *sp != '\t') sp++; while (*sp == ' ' || *sp == '\t') sp++; const char *se = sp; while (*se && *se != ' ' && *se != '\t' && *se != '\n' && *se != '\r') se++; const char *bp = se; while (*bp == ' ' || *bp == '\t') bp++; const char *be = bp; while (*be && *be != ' ' && *be != '\t' && *be != '\n' && *be != '\r') be++; if (sp != se) { char raw[160]; int l = (int)(se - sp); if (l > (int)sizeof raw - 1) l = (int)sizeof raw - 1; memcpy(raw, sp, (size_t)l); raw[l] = 0; out[n].id = id; nm_decode_ssid(raw, out[n].name, sizeof out[n].name); if (be > bp && (be - bp) < (int)sizeof out[n].mac) { memcpy(out[n].mac, bp, (size_t)(be - bp)); out[n].mac[be - bp] = 0; } out[n].sec[0] = 0; n++; } } line = eol ? eol + 1 : NULL; } return n; } /* —— 解析 netmgrd `wifi scan`(wpa_cli scan_results 原样文本)→ meow_net_t[](按 BSSID 去重、 * 优先具名;补充 sec 安全类型供「新网络录入」判断是否弹 IME)。返回项数。 —— */ static int nm_parse_scan(const char *txt, meow_net_t *out, int max) { int n = 0; if (!txt || max <= 0) return 0; if (max > S_NET_MAX) max = S_NET_MAX; char seen_bssid[S_NET_MAX][24]; int named[S_NET_MAX] = {0}; const char *line = strchr(txt, '\n'); if (line) line++; while (line && *line && n < max) { const char *eol = strchr(line, '\n'); const char *p = line; while (*p == ' ' || *p == '\t') p++; if (*p) { char bssid[24] = ""; { const char *b = p; while (*p && *p != ' ' && *p != '\t') p++; int l = (int)(p - b); if (l > 22) l = 22; memcpy(bssid, b, (size_t)l); bssid[l] = 0; } const char *ssid = ""; char sec[16] = "Open"; const char *br = strchr(p, '['); if (br) { /* 安全类型 = 首个方括号内容([WPA-PSK-CCMP] 等);纯 [ESS]/[IBSS] = 开放 */ const char *be = strchr(br + 1, ']'); if (be) { int sl = (int)(be - (br + 1)); if (sl > 15) sl = 15; if (sl > 0) { memcpy(sec, br + 1, (size_t)sl); sec[sl] = 0; if (!strstr(sec, "WPA") && !strstr(sec, "RSN") && !strstr(sec, "WEP") && !strstr(sec, "SAE") && !strstr(sec, "EAP")) snprintf(sec, sizeof sec, "Open"); } } const char *q = br; while (*q) { while (*q == ' ' || *q == '\t') q++; if (*q == '[') { while (*q && *q != ' ' && *q != '\t') q++; } else break; } ssid = q; while (*ssid == ' ' || *ssid == '\t') ssid++; } const char *se = ssid; while (*se && *se != '\n' && *se != '\r') se++; while (se > ssid && (se[-1] == ' ' || se[-1] == '\t')) se--; int has = (se > ssid && *ssid); int idx = -1; for (int j = 0; j < n; j++) if (!strcmp(seen_bssid[j], bssid)) { idx = j; break; } if (idx < 0) { if (n >= max) { line = eol ? eol + 1 : NULL; continue; } idx = n++; strncpy(seen_bssid[idx], bssid, sizeof seen_bssid[idx] - 1); seen_bssid[idx][sizeof seen_bssid[idx] - 1] = 0; named[idx] = has; out[idx].id = -1; snprintf(out[idx].mac, sizeof out[idx].mac, "%s", bssid); snprintf(out[idx].sec, sizeof out[idx].sec, "%s", sec); if (has) { char raw[96]; int l = (int)(se - ssid); if (l > (int)sizeof raw - 1) l = (int)sizeof raw - 1; memcpy(raw, ssid, (size_t)l); raw[l] = 0; nm_decode_ssid(raw, out[idx].name, sizeof out[idx].name); } else { snprintf(out[idx].name, sizeof out[idx].name, "(隐藏网络)"); } } else if (has && !named[idx]) { char raw[96]; int l = (int)(se - ssid); if (l > (int)sizeof raw - 1) l = (int)sizeof raw - 1; memcpy(raw, ssid, (size_t)l); raw[l] = 0; nm_decode_ssid(raw, out[idx].name, sizeof out[idx].name); named[idx] = 1; } } line = eol ? eol + 1 : NULL; } return n; } int nm_wifi_saved(meow_net_t *out, int max) { char text[MEOW_IPC_MAX - 32]; int rc = netmgr_wifi("networks", NULL, text, sizeof text); if (rc == NETMGR_UNREACH) return net_wifi_saved(out, max); /* 直连回退 */ if (rc != MEOW_OK) return 0; return nm_parse_saved(text, out, max); } int nm_wifi_scan(meow_net_t *out, int max) { char text[MEOW_IPC_MAX - 32]; int rc = netmgr_wifi("scan", NULL, text, sizeof text); /* netmgrd 内含触发+有界等待+读结果 */ if (rc == NETMGR_UNREACH) return net_wifi_scan(out, max); if (rc != MEOW_OK) return 0; return nm_parse_scan(text, out, max); } void nm_wifi_scan_trigger(void) { /* IPC:scan 子命令在 netmgrd 内已一次性「触发+等待+读结果」,此处无需单独触发; * 直连回退:异步 wpa_cli scan(结果稍后 net_wifi_scan 读)。 */ if (!netmgr_ok()) net_wifi_scan_trigger(); } /* —— 直连回退:wpa_cli add/set/enable/select 新网络(复刻 netmgrd wifi_connect 逻辑)—— */ static int nm_direct_connect(const char *ssid, const char *psk, char *err, int elen) { const char *wpa = "PATH=/bin:/sbin:/usr/bin:/usr/sbin wpa_cli -i wlan0"; char cmd[256]; snprintf(cmd, sizeof cmd, "%s add_network 2>/dev/null", wpa); char *o = popen_read_tmo(1200, cmd); int id = o ? atoi(o) : -1; if (o) free(o); if (id < 0) { if (err) snprintf(err, elen, "add_network 无 id"); return MEOW_ERR_INVAL; } { char *r; snprintf(cmd, sizeof cmd, "%s set_network %d ssid '\"%s\"' 2>/dev/null", wpa, id, ssid); if ((r = popen_read_tmo(1200, cmd))) free(r); if (psk && psk[0]) { snprintf(cmd, sizeof cmd, "%s set_network %d psk '\"%s\"' 2>/dev/null", wpa, id, psk); if ((r = popen_read_tmo(1200, cmd))) free(r); } else { snprintf(cmd, sizeof cmd, "%s set_network %d key_mgmt NONE 2>/dev/null", wpa, id); if ((r = popen_read_tmo(1200, cmd))) free(r); } snprintf(cmd, sizeof cmd, "%s enable_network %d 2>/dev/null", wpa, id); if ((r = popen_read_tmo(1200, cmd))) free(r); snprintf(cmd, sizeof cmd, "%s select_network %d 2>/dev/null", wpa, id); if ((r = popen_read_tmo(1200, cmd))) free(r); } if (err) snprintf(err, elen, "connect: ssid=%s network_id=%d", ssid, id); logmsg("netmgr_ipc: [direct-fallback] wifi connect ssid=%s id=%d", ssid, id); return MEOW_OK; } int nm_wifi_connect(const char *ssid, const char *psk, char *err, int elen) { if (!ssid || !ssid[0]) { if (err) snprintf(err, elen, "ssid 缺失"); return MEOW_ERR_INVAL; } char arg[132]; snprintf(arg, sizeof arg, "%s,%s", ssid, psk ? psk : ""); /* connect 带空 psk → netmgrd 复用既有网络 */ char text[192]; int rc = netmgr_wifi("connect", arg, text, sizeof text); if (rc == NETMGR_UNREACH) return nm_direct_connect(ssid, psk, err, elen); if (err && elen > 0) snprintf(err, elen, "%s", text); return rc; } int nm_wifi_disconnect(char *err, int elen) { char text[128]; int rc = netmgr_wifi("disconnect", NULL, text, sizeof text); if (rc == NETMGR_UNREACH) { char cmd[160]; snprintf(cmd, sizeof cmd, "PATH=/bin:/sbin:/usr/bin:/usr/sbin wpa_cli -i wlan0 disconnect 2>/dev/null"); popen_read_tmo(1200, cmd); if (err) snprintf(err, elen, "已下发"); return MEOW_OK; } if (err && elen > 0) snprintf(err, elen, "%s", text); return rc; } int nm_wifi_toggle(int on) { char text[128]; int rc = netmgr_wifi(on ? "on" : "off", NULL, text, sizeof text); if (rc == NETMGR_UNREACH) { s_net_toggle_wifi(on); return MEOW_OK; } /* 直连回退 */ return rc; } /* ================= BT(IPC 优先:on/off/status/paired;其余子命令 netmgrd 无,直连)================= */ int nm_bt_powered(void) { char text[128]; int rc = netmgr_bt("status", NULL, text, sizeof text); if (rc == NETMGR_UNREACH) return hci_powered(); if (rc != MEOW_OK) return 0; char v[8]; nm_val(text, "powered=", v, sizeof v, 0); return atoi(v); } int nm_bt_onoff(int on) { char text[128]; int rc = netmgr_bt(on ? "on" : "off", NULL, text, sizeof text); if (rc == NETMGR_UNREACH) { s_net_toggle_bt(on); return MEOW_OK; } return rc; } int nm_bt_status(int *powered, int *paired_n, char *addr, int alen) { char text[192]; int rc = netmgr_bt("status", NULL, text, sizeof text); if (rc == NETMGR_UNREACH) { /* 直连回退:读内核/tool */ if (powered) *powered = hci_powered(); if (paired_n) *paired_n = -1; if (addr && alen > 0) addr[0] = 0; return rc; } if (rc == MEOW_OK) { char v[8]; if (powered) { nm_val(text, "powered=", v, sizeof v, 0); *powered = atoi(v); } if (paired_n) { nm_val(text, "paired=", v, sizeof v, 0); *paired_n = atoi(v); } if (addr && alen > 0) nm_val(text, "address=", addr, alen, 0); } return rc; } int nm_bt_paired(meow_net_t *out, int max) { char text[MEOW_IPC_MAX - 32]; int rc = netmgr_bt("paired", NULL, text, sizeof text); if (rc == NETMGR_UNREACH) return net_bt_paired(out, max); if (rc != MEOW_OK) return 0; int n = 0; if (max > S_NET_MAX) max = S_NET_MAX; if (text[0]) { char *line = text; while (line && *line && n < max) { char *eol = strchr(line, '\n'); if (eol) *eol = 0; char *p = strstr(line, "Device "); if (p) { p += 7; char *mac = p; while (*mac && *mac != ' ' && *mac != '\t') mac++; char *nm2 = mac; while (*nm2 == ' ' || *nm2 == '\t') nm2++; int ml = (int)(mac - p); if (ml > 47) ml = 47; if (ml > 0 && ml < (int)sizeof out[n].mac) { memcpy(out[n].mac, p, (size_t)ml); out[n].mac[ml] = 0; } snprintf(out[n].name, sizeof out[n].name, "%s", *nm2 ? nm2 : p); out[n].id = -1; out[n].sec[0] = 0; n++; } line = eol ? eol + 1 : NULL; } } return n; }