diff --git a/patch_v2/meowpanel/meowpanel b/patch_v2/meowpanel/meowpanel index 693a7c4..9b0768f 100755 Binary files a/patch_v2/meowpanel/meowpanel and b/patch_v2/meowpanel/meowpanel differ diff --git a/patch_v2/meowpanel/meowpanel.c b/patch_v2/meowpanel/meowpanel.c index 9de2d58..ec35d80 100644 --- a/patch_v2/meowpanel/meowpanel.c +++ b/patch_v2/meowpanel/meowpanel.c @@ -93,6 +93,7 @@ static const char *g_notif_log = NULL; /* 通知转储日志:/notificat #define POWER_SOCK "/usr/data/meow_power.sock" #define LAUNCHER_SOCK "/usr/data/meow_ipc.sock" /* launcher 主 IPC(通知活跃集查询/撤清) */ #define AUDIO_SOCK "/usr/data/meow_audio.sock" /* meow_audiod 状态快照(MSG_AUDIO_QUERY→STATE) */ +#define NET_SOCK "/usr/data/meow_net.sock" /* meow_netmgrd 网络管理(MSG_NET_*,期2 观测层/DNS 代理) */ #define NYAI_PATH "/usr/bin/nyai" /* 固件内置(权威) */ #define NYAI_PATH_CARD "/usr/data/mnt/sd_0/MeowOS/opt/bin/nyai" /* 旧卡上路径(回退) */ #define BOOT_DEFAULT_FILE "/data/meow_boot_default" @@ -125,6 +126,7 @@ static const char *g_sock_perf = PERF_SOCK; static const char *g_sock_power = POWER_SOCK; static const char *g_sock_launcher = LAUNCHER_SOCK; static const char *g_sock_audio = AUDIO_SOCK; +static const char *g_sock_net = NET_SOCK; /* meow_netmgrd(--sock-net / env MEOW_NET_SOCK 覆盖) */ /* ---------------- 增量帧缓存(GET /api/screen.jpeg?delta=1,阶段 C 增强) ---------------- * 服务端缓存上一帧紧凑 RGB565,按 8×8 块比对,只编码变化块(外接 bbox 子图)并以响应头回传偏移。 @@ -234,6 +236,8 @@ static int send_err(int sock, int status, const char *code, const char *msg); static int send_json(int sock, int status, const char *data); static int send_file(int sock, int status, const char *ctype, const char *abspath, const char *filename); static void panel_log(const char *fmt, ...); +static int qparam(const char *query, const char *key, char *out, int outcap); /* 定义在文件后部;网络连接表 query 取值 */ +static int tf_present(void); /* 定义在文件后部;net 日志卡根判定 */ static const char *web_root(void); /* 定义在文件后部;此处前向声明供鉴权会话路径使用 */ /* ---------------- CSV(enum 名/标签)非改写取值;定义在设置列表区,先声明供 settings_coerce 用 ---------------- */ @@ -976,6 +980,242 @@ static int api_audio(int sock) return rc; } +/* ---------------- 网络管理:meow_netmgrd IPC(MSG_NET_*,期2 观测层/DNS 代理) ---------------- + * 收口于独立 socket /usr/data/meow_net.sock(--sock-net / env MEOW_NET_SOCK 覆盖,宿主测试用)。 + * 读级(STATUS/CONNS/DNS status|stats)netmgrd 放行任意调用者;flush 写级——HTTP 层沿用面板 + * 鉴权门禁(require_auth),IPC 层身份由 netmgrd 依 SO_PEERCRED 判定(面板路径非系统身份时 + * netmgrd 会拒 flush,面板如实回传其错误码/文本,不掩盖)。 + * netmgrd 不在/超时/应答异常 → 503 降级 JSON({"ok":false,...}),面板其它页不受影响。 */ +static int net_ipc(uint16_t req_type, const void *req, uint32_t req_len, + uint16_t *ret_type, uint32_t *code, uint16_t *meta, + char *out, size_t cap) +{ + int fd = ipc_connect_tmo(g_sock_net, IPC_TIMEOUT_MS); + if (fd < 0) return -1; + uint8_t buf[MEOW_IPC_MAX]; + uint16_t type = 0; uint32_t rid = 0; + int rlen = -1; + if (send_frame(fd, req_type, 0x1020, req, req_len) == 0) + rlen = recv_frame(fd, &type, &rid, buf, sizeof buf); + close(fd); + if (rlen <= 0) return -1; + if (ret_type) *ret_type = type; + /* m_net_*_result 统一布局:code(u32)|len(u16)|meta/pad(u16)|content[len],content 偏移 8 */ + uint32_t c = 0; uint16_t ln = 0, mv = 0; + memcpy(&c, buf, 4); memcpy(&ln, buf + 4, 2); memcpy(&mv, buf + 6, 2); + if (code) *code = c; + if (meta) *meta = mv; + if ((size_t)ln >= cap) ln = (uint16_t)(cap - 1); + if ((size_t)(8 + ln) < sizeof buf) buf[8 + ln] = 0; + memcpy(out, buf + 8, ln); + out[ln] = 0; + return (int)ln; +} + +/* GET /api/net/status → 透传 0xE0 概览 JSON(接口表 + WiFi/BT 摘要 + 接口速率)。 */ +static int api_net_status(int sock) +{ + uint16_t t; uint32_t code; char json[MEOW_IPC_MAX]; + int n = net_ipc(MSG_NET_STATUS_REQ, NULL, 0, &t, &code, NULL, json, sizeof json); + if (n <= 0 || t != MSG_NET_STATUS_RESULT || code != MEOW_OK || !json[0]) + return send_err(sock, 503, "NET_UNAVAILABLE", "网络管理服务不可用"); + return send_json(sock, 200, json); +} + +/* 连接表一行:entity|lip:lport|rip:rport|state|age_s|domain(netmgrd build_conns_text)。 + * 把每条构造成 `{...}` 对象、逗号分隔(不含外层 [])写入 sbuf,返回条数。 */ +static int net_conns_parse_body(sbuf_t *s, const char *txt) +{ + const char *p = txt; + int first = 1, n = 0; + while (p && *p) { + const char *nl = strchr(p, '\n'); + size_t ll = nl ? (size_t)(nl - p) : strlen(p); + if (ll == 0) { p = nl ? nl + 1 : p + 1; continue; } + char line[384]; + if (ll >= sizeof line) ll = sizeof line - 1; + memcpy(line, p, ll); line[ll] = 0; + char *f[6]; int fn = 0; char *cur = line; + while (fn < 6) { + f[fn++] = cur; + char *bar = strchr(cur, '|'); + if (!bar) break; + *bar = 0; cur = bar + 1; + } + if (fn >= 6) { + if (!first) sA(s, ","); + first = 0; + sA(s, "{\"entity\":"); sA_str(s, f[0]); + sA(s, ",\"local\":"); sA_str(s, f[1]); + sA(s, ",\"remote\":"); sA_str(s, f[2]); + sA(s, ",\"state\":"); sA_str(s, f[3]); + sA(s, ",\"age\":%lld,\"domain\":", atoll(f[4])); + sA_str(s, strcmp(f[5], "-") == 0 ? "" : f[5]); + sA(s, "}"); + n++; + } + p = nl ? nl + 1 : p + ll; + } + return n; +} + +/* GET /api/net/conns?entity=<可选> → 0xE8 → 连接表 JSON(可实体前缀过滤)。 */ +static int api_net_conns(int sock, const char *query) +{ + m_net_conns_req_t q; memset(&q, 0, sizeof q); + char ent[128]; + if (qparam(query, "entity", ent, sizeof ent)) + strncpy(q.entity, ent, sizeof q.entity - 1); + uint16_t t; uint32_t code; uint16_t meta; char txt[MEOW_IPC_MAX]; + int n = net_ipc(MSG_NET_CONNS_REQ, &q, sizeof q, &t, &code, &meta, txt, sizeof txt); + if (n <= 0 || t != MSG_NET_CONNS_RESULT || code != MEOW_OK) + return send_err(sock, 503, "NET_UNAVAILABLE", "网络管理服务不可用"); + sbuf_t a = {0}; + int count = net_conns_parse_body(&a, txt); + sbuf_t s = {0}; + sA(&s, "{\"truncated\":%s,\"count\":%d,\"conns\":[", meta ? "true" : "false", count); + if (a.n) sA_append(&s, a.b, a.n); + sA(&s, "]}"); + sbuf_free(&a); + int rc = send_json(sock, 200, s.b ? s.b : ""); + sbuf_free(&s); + return rc; +} + +/* GET /api/net/dns → 0xEC status(netmgrd 对 status/stats 同输出)→ DNS 代理统计 JSON。 */ +static int api_net_dns(int sock) +{ + m_net_dns_ctl_t q; memset(&q, 0, sizeof q); + strcpy(q.cmd, "status"); + uint16_t t; uint32_t code; uint16_t meta; char txt[MEOW_IPC_MAX]; + int n = net_ipc(MSG_NET_DNS_CTL, &q, sizeof q, &t, &code, &meta, txt, sizeof txt); + if (n <= 0 || t != MSG_NET_DNS_RESULT) + return send_err(sock, 503, "NET_UNAVAILABLE", "网络管理服务不可用"); + if (code != MEOW_OK) + return send_err(sock, 503, "NET_NODEV", txt[0] ? txt : "DNS 代理不可用"); + /* txt: `dns mode=%s enabled=%d upstream=%s cache=%d/%d q=%llu deny=%llu first=%llu resolv_modified=%d` */ + char mode[32] = "", upstream[64] = ""; + int enabled = 0, ch = 0, ct = 0, rmod = 0; + unsigned long long qq = 0, deny = 0, first = 0; + (void)sscanf(txt, "dns mode=%31s enabled=%d upstream=%63s cache=%d/%d q=%llu deny=%llu first=%llu resolv_modified=%d", + mode, &enabled, upstream, &ch, &ct, &qq, &deny, &first, &rmod); + sbuf_t s = {0}; + sA(&s, "{\"mode\":"); + sA_str(&s, mode); + sA(&s, ",\"enabled\":%s,\"upstream\":", enabled ? "true" : "false"); + sA_str(&s, upstream); + sA(&s, ",\"cache_hits\":%d,\"cache_total\":%d,\"queries\":%llu,\"denies\":%llu,\"first_seen\":%llu,\"resolv_modified\":%s}", + ch, ct, qq, deny, first, rmod ? "true" : "false"); + int rc = send_json(sock, 200, s.b ? s.b : ""); + sbuf_free(&s); + return rc; +} + +/* POST /api/net/dns/flush → 0xEC flush(写级:HTTP 层走 require_auth;IPC 层由 netmgrd 依身份判定)。 */ +static int api_net_dns_flush(int sock) +{ + m_net_dns_ctl_t q; memset(&q, 0, sizeof q); + strcpy(q.cmd, "flush"); + uint16_t t; uint32_t code; uint16_t meta; char txt[MEOW_IPC_MAX]; + int n = net_ipc(MSG_NET_DNS_CTL, &q, sizeof q, &t, &code, &meta, txt, sizeof txt); + if (n <= 0 || t != MSG_NET_DNS_RESULT) + return send_err(sock, 503, "NET_UNAVAILABLE", "网络管理服务不可用"); + if (code == (uint32_t)MEOW_ERR_PERM) + return send_err(sock, 403, "FORBIDDEN", txt[0] ? txt : "DNS flush 需系统身份"); + if (code != MEOW_OK) + return send_err(sock, 503, "NET_NODEV", txt[0] ? txt : "DNS flush 失败"); + /* txt: `dns cache flushed (N entries)` */ + int entries = 0; + (void)sscanf(txt, "dns cache flushed (%d entries)", &entries); + sbuf_t s = {0}; + sA(&s, "{\"flushed\":true,\"entries\":%d}", entries); + int rc = send_json(sock, 200, s.b ? s.b : ""); + sbuf_free(&s); + return rc; +} + +#define NET_LOG_TAIL_MAX (256 * 1024) /* net 日志 tail 读取上限(内存约束:不整读 64MB 滚动文件) */ + +/* GET /api/net/log → 读 TF MeowOS/logs/ 最新的 net_.log 尾部(同日志页模式)。 */ +static int api_net_log(int sock) +{ + if (!tf_present()) return send_err(sock, 503, "NO_CARD", "未识别到 TF 卡"); + DIR *d = opendir(LOGS_DIR); + if (!d) return send_json(sock, 200, "{\"file\":\"\",\"lines\":[],\"total\":0,\"truncated\":false}"); + char best[512] = ""; long best_mt = -1; + struct dirent *e; + while ((e = readdir(d))) { + if (strncmp(e->d_name, "net_", 4) != 0) continue; + if (e->d_name[0] == '.') continue; + char full[1024]; + snprintf(full, sizeof full, "%s/%s", LOGS_DIR, e->d_name); + struct stat st; + if (stat(full, &st) < 0 || !S_ISREG(st.st_mode)) continue; + if (st.st_mtime > best_mt) { best_mt = st.st_mtime; snprintf(best, sizeof best, "%s", e->d_name); } + } + closedir(d); + if (!best[0]) + return send_json(sock, 200, "{\"file\":\"\",\"lines\":[],\"total\":0,\"truncated\":false}"); + char full[1024]; + snprintf(full, sizeof full, "%s/%s", LOGS_DIR, best); + int fd = open(full, O_RDONLY); + struct stat st; + if (fd < 0 || fstat(fd, &st) < 0 || !S_ISREG(st.st_mode)) { + if (fd >= 0) close(fd); + return send_json(sock, 200, "{\"file\":\"\",\"lines\":[],\"total\":0,\"truncated\":false}"); + } + off_t off = st.st_size > NET_LOG_TAIL_MAX ? st.st_size - NET_LOG_TAIL_MAX : 0; + size_t buflen = (size_t)(st.st_size - off); + if (buflen == 0) { close(fd); return send_json(sock, 200, "{\"file\":\"\",\"lines\":[],\"total\":0,\"truncated\":false}"); } + if (off) (void)lseek(fd, off, SEEK_SET); + char *buf = malloc(buflen + 1); + if (!buf) { close(fd); return send_err(sock, 500, "INTERNAL", "内存不足"); } + ssize_t n = read(fd, buf, buflen); + close(fd); + if (n <= 0) { free(buf); return send_json(sock, 200, "{\"file\":\"\",\"lines\":[],\"total\":0,\"truncated\":false}"); } + sbuf_t s = {0}; + sA(&s, "{\"file\":"); + sA_str(&s, best); + sA(&s, ",\"lines\":["); + const char *p = buf, *end = buf + n; + int linecnt = 0; + for (const char *c = buf; c < end; c++) if (*c == '\n') linecnt++; + if (linecnt > 0 && end[-1] != '\n') linecnt++; + int maxlines = LOG_MAX_LINES; + int startrow = linecnt - maxlines; + if (startrow < 0) startrow = 0; + int truncated = (off > 0 || startrow > 0) ? 1 : 0; + int rowidx = 0, gotlines = 0; + p = buf; + while (p < end) { + const char *nl = memchr(p, '\n', (size_t)(end - p)); + const char *line_end = nl ? nl : end; + if (rowidx >= startrow) { + if (gotlines) sA(&s, ","); + sbuf_t ln = {0}; + for (const char *c = p; c < line_end; c++) { + unsigned char cc = (unsigned char)*c; + if (cc == '"' || cc == '\\') sA(&ln, "\\%c", (int)cc); + else if (cc < 0x20 && cc != '\t') sA(&ln, "\\u%04x", cc); + else sA(&ln, "%c", (int)cc); + } + sA(&s, "\""); + for (const char *c = ln.b ? ln.b : ""; *c; c++) sA(&s, "%c", (int)(unsigned char)*c); + sA(&s, "\""); + sbuf_free(&ln); + gotlines++; + } + rowidx++; + if (!nl) break; + p = nl + 1; + } + free(buf); + sA(&s, "],\"total\":%d,\"truncated\":%s}", linecnt, truncated ? "true" : "false"); + int rc = send_json(sock, 200, s.b ? s.b : ""); + sbuf_free(&s); + return rc; +} + /* ---------------- 路径安全 ---------------- */ static int tf_present(void) { @@ -4456,6 +4696,12 @@ static int handle(int sock, req_t *r) /* ---- 音频状态(meow_audiod 快照,只读) ---- */ if (!strcmp(r->method, "GET") && !strcmp(p, "/api/audio")) return api_audio(sock); + /* ---- 网络管理(meow_netmgrd 期2 透传:读级任意调用者;flush 写级走 require_auth) ---- */ + if (!strcmp(r->method, "GET") && !strcmp(p, "/api/net/status")) return api_net_status(sock); + if (!strcmp(r->method, "GET") && !strcmp(p, "/api/net/conns")) return api_net_conns(sock, r->query); + if (!strcmp(r->method, "GET") && !strcmp(p, "/api/net/dns")) return api_net_dns(sock); + if (!strcmp(r->method, "GET") && !strcmp(p, "/api/net/log")) return api_net_log(sock); + /* ---- 实时画面(阶段 C) ---- */ if (!strcmp(r->method, "GET") && !strcmp(p, "/api/screen.jpeg")) return api_screen_jpeg(sock, r); @@ -4473,6 +4719,7 @@ static int handle(int sock, req_t *r) if (!strcmp(p, "/api/files/rename")) return api_files_rename(sock, r); if (!strcmp(p, "/api/apps/install")) return api_apps_install(sock, r); if (!strcmp(p, "/api/notifications/dismiss_all") || !strcmp(p, "/api/notifications/clear")) return api_notification_dismiss_all(sock); + if (!strcmp(p, "/api/net/dns/flush")) return api_net_dns_flush(sock); if (!strcmp(p, "/api/key")) return api_key_batch(sock, r); if (!strcmp(p, "/api/input/tap")) return api_input_tap(sock, r); char seg[256]; @@ -4585,10 +4832,11 @@ int main(int argc, char **argv) else if (!strcmp(argv[i], "--sock-power") && i + 1 < argc) g_sock_power = argv[++i]; else if (!strcmp(argv[i], "--sock-launcher") && i + 1 < argc) g_sock_launcher = argv[++i]; else if (!strcmp(argv[i], "--sock-audio") && i + 1 < argc) g_sock_audio = argv[++i]; + else if (!strcmp(argv[i], "--sock-net") && i + 1 < argc) g_sock_net = argv[++i]; else if (!strcmp(argv[i], "--sock") && i + 1 < argc) { g_sock_perf = argv[++i]; g_sock_power = g_sock_perf; } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { printf("MeowPanel backend %s\n", PANEL_VERSION); - printf("usage: meowpanel [-p PORT] [--sock-perf P] [--sock-power P] [--sock-launcher P] [--sock-audio P]\n"); + printf("usage: meowpanel [-p PORT] [--sock-perf P] [--sock-power P] [--sock-launcher P] [--sock-audio P] [--sock-net P]\n"); return 0; } } @@ -4601,6 +4849,8 @@ int main(int argc, char **argv) if (env && env[0]) g_sock_launcher = env; env = getenv("MEOW_AUDIO_SOCK"); if (env && env[0]) g_sock_audio = env; /* 与 audiod CLI 同 env(宿主/qemu 测试用) */ + env = getenv("MEOW_NET_SOCK"); + if (env && env[0]) g_sock_net = env; /* 与 netmgrd/meow_net 同 env(宿主/qemu 测试用) */ env = getenv("MEOW_SETTINGS_FILE"); if (env && env[0]) g_settings_file = env; /* 设置文件覆盖(宿主测试用;设备默认 /usr/data/meow/settings.conf) */ diff --git a/patch_v2/meowpanel/meowpanel_host b/patch_v2/meowpanel/meowpanel_host index 62db04a..6281466 100755 Binary files a/patch_v2/meowpanel/meowpanel_host and b/patch_v2/meowpanel/meowpanel_host differ diff --git a/patch_v2/meowpanel/web/index.html b/patch_v2/meowpanel/web/index.html index 5ed49bc..f16b695 100644 --- a/patch_v2/meowpanel/web/index.html +++ b/patch_v2/meowpanel/web/index.html @@ -33,6 +33,10 @@ 音频 + ' + + ''; + } + + async function fetchNetOnce() { + if (state.route !== 'net') return; + const stEl = $('#net-status'), cbEl = $('#net-conns-body'), dbEl = $('#net-dns-body'); + let st, cn, dn; + try { st = await api('GET', '/api/net/status'); } + catch (e) { netUnavailable(errText(e)); return; } + try { cn = await api('GET', '/api/net/conns' + (netEntityFilter ? '?entity=' + encodeURIComponent(netEntityFilter) : '')); } + catch (e) { cn = null; } + try { dn = await api('GET', '/api/net/dns'); } + catch (e) { dn = null; } + if (state.route !== 'net') return; + if (stEl) stEl.innerHTML = netStatusCards(st); + if (cbEl) cbEl.innerHTML = netConnsBody(cn); + if (dbEl) dbEl.innerHTML = netDnsBody(dn); + } + + async function fetchNetConns() { + if (state.route !== 'net') return; + const cbEl = $('#net-conns-body'); if (!cbEl) return; + let cn; + try { + cn = await api('GET', '/api/net/conns' + (netEntityFilter ? '?entity=' + encodeURIComponent(netEntityFilter) : '')); + } catch (e) { cn = null; } + if (state.route !== 'net') return; + cbEl.innerHTML = netConnsBody(cn); + } + + async function fetchNetLog() { + if (state.route !== 'net') return; + const el = $('#net-log-body'); + try { + const d = await api('GET', '/api/net/log'); + if (el) { + if (!d || !d.file) { el.innerHTML = '
暂无 net_ 日志' + (d && !d.file ? '(netmgrd 未写日志)' : '') + '
'; return; } + el.innerHTML = '
' + esc((d.lines || []).join('\n') || '(空)') +
+          (d.truncated ? '\n…(超出尾部上限,部分被截断)' : '') + '
'; + const name = $('#net-log-file'); if (name) name.textContent = d.file; + } + } catch (e) { if (el) el.innerHTML = '
日志加载失败:' + esc(errText(e)) + '
'; } + } + + async function renderNet() { + stopNet(); + netEntityFilter = ''; + $('#content').innerHTML = + '
' + + '

加载网络状态…

' + + '

每实体连接表

' + + '
' + + '' + + '
' + + '

加载连接表…

' + + '

DNS 代理

' + + '
' + + '
' + + '

加载 DNS 统计…

' + + '

网络日志

' + + '
' + + '

加载日志…

' + + '
'; + netTimer = setInterval(fetchNetOnce, 5000); // status 失败时 netUnavailable 会 stopNet() 清掉 + fetchNetOnce(); // 填充 status + conns + dns + fetchNetLog(); + } + + function bindNet() { + $('#content').addEventListener('input', function (e) { + const f = e.target.closest('#net-entity-filter'); if (!f) return; + netEntityFilter = f.value.trim(); + const cb = $('#net-conns-body'); if (cb) cb.textContent = '筛选…'; + fetchNetConns(); + }); + $('#content').addEventListener('click', async function (e) { + if (e.target.closest('#net-conns-refresh')) { fetchNetConns(); return; } + if (e.target.closest('#net-log-refresh')) { fetchNetLog(); return; } + if (e.target.closest('#net-retry')) { renderNet(); return; } + const flush = e.target.closest('#net-dns-flush'); + if (flush) { + flush.disabled = true; + try { + const d = await api('POST', '/api/net/dns/flush'); + toast('已清空 DNS 缓存(' + (d && d.entries != null ? d.entries : '') + ' 条)'); + } catch (err) { toast('清缓存失败:' + errText(err)); } + flush.disabled = false; + fetchNetOnce(); + } + }); + } + /* ---------------- 3. 文件管理页 ---------------- */ // 返回带索引的 rel 显示面包屑 function joinRel(a, b) { @@ -1258,6 +1471,7 @@ perf: { title: '实时性能', render: renderPerf, bind: null }, bright: { title: '亮度 · 音量', render: renderBright, bind: null }, audio: { title: '音频状态', render: renderAudio, bind: null }, + net: { title: '网络管理', render: renderNet, bind: bindNet }, files: { title: '文件管理', render: renderFiles, bind: bindFiles }, apps: { title: 'MeowAPPs 管理', render: renderApps, bind: bindApps }, fonts: { title: '字体管理', render: renderFontsPage, bind: bindFonts }, @@ -1280,6 +1494,7 @@ state.route = id; stopPerf(); stopAudio(); + stopNet(); stopScreenPoll(); $$('.nav-item').forEach(b => b.classList.toggle('active', b.dataset.route === id)); $('#page-title').textContent = r.title; @@ -1299,7 +1514,7 @@ bindNav(); bindThemeUi(); bindSlider(); - bindFiles(); bindApps(); bindFonts(); bindSettings(); bindRemote(); bindScreen(); bindSystem(); bindLogs(); bindNotif(); + bindFiles(); bindApps(); bindFonts(); bindSettings(); bindRemote(); bindScreen(); bindSystem(); bindLogs(); bindNotif(); bindNet(); bindLogin(); bindPwDlg(); // 首次进入探鉴权状态:启用且无有效会话 → 登录页;否则直接进应用