fix(meowpanel §UTF-8): JSON 输出 UTF-8 加固——sA_str 统一消毒非法/截断字节,防任一上游坏字节拖垮整页

事故:amsd 的 am list 把「流媒体客户端」(18B) 截成 16B,腰斩「端」字(E7 AB AF→仅剩 E7) →
面板 ams_fill 把非法 UTF-8 嵌进 /api/overview → 浏览器 JSON.parse 拒收(HTTP 200 非 JSON)。
amsd 侧已修(8588a6b),面板侧自此防住任一上游再产坏字节,一个源的坏数据不拖垮整页。

- meowpanel.c: 新增 utf8_seq_len/utf8_seq_ok/utf8_valid_n/utf8_sanitize(C 无依赖);sA_str 收口为
  sA_str_n:逐字节校验 UTF-8,合法序列透传、非法/截断逐字节回退 '?'(保持长度);纯 ASCII 单遍直通。
- 覆盖点(全部经 sA_str/sA_str_n):ams_fill(pkg/name)、svcd_fill(name/exec/state/...)、audio_fill(app/fmt/route_mode)、
  /api/apps(app.json name/pkg/ver/exec)、字体名、通知字段、崩溃/日志中心字段、设置/主题串等。
- 两处日志内联转义(api_net_log、api_logs_read)改走 sA_str_n,日志行同样消毒。
- 原始 IPC JSON(perf/power/net,含 ssid)先 utf8_valid_n 校验:坏字节时 perf/power/net/status 降级
  available:false / 503,不嵌坏字节(api_overview 首页生命线不受影响;api_system network 段坏字节回退 hostname_local)。
- 宿主自测 `--selftest`:喂「流媒体客户\xE7」给 utf8_sanitize/sA_str/ams_fill 桩,断言 JSON 完整可解析 + 无非法字节,
  且不误改完整合法名「流媒体客户端」;`meowpanel --selftest` 通过。
- 验证:build_meowpanel.sh host / 交叉均 -Werror 0 warning;真实 ams_fill 桩复现事故 → 「流媒体客户?」JSON 合法。
- 后端 meowpanel(mipsel) md5: 820b24541ed69f2e27e4a301aff10c98(README 已校正)
This commit is contained in:
AskaEth committed 2026-09-02 02:32:25 +08:00
1 parent 3397a702aa
commit d7bf62f448
4 files changed
+174 -39

No files matched your search

+1 -1
View File
@@ -28,7 +28,7 @@ cd /home/Aska/Project/R3ProII/patch_v2/meowpanel
- 两分支均以 `-Os -Wall -Wextra` 复核,**0 warning**(`-Werror` 亦通过)。 - 两分支均以 `-Os -Wall -Wextra` 复核,**0 warning**(`-Werror` 亦通过)。
- 产出(本次构建实测): - 产出(本次构建实测):
- `meowpanel`:mipsel 静态,`ELF 32-bit LSB executable, MIPS, MIPS32 rel2, statically linked, stripped`, - `meowpanel`:mipsel 静态,`ELF 32-bit LSB executable, MIPS, MIPS32 rel2, statically linked, stripped`,
体积约 **256 KB**(固件阈值内)。md5:`aed2ea17a34614acaa26dcbc8965ebb1`(批2:崩溃/日志中心 `/api/logs/crash` + 日志分组/`group`;`/api/system` 补 `reboot_reason/upgrade_status`;`/api/overview` net 块并入 DNS 统计;音频**写级** `/api/audio/route`(仅 0/2/3,拒 1/4/5)+ `/api/audio/volume`(clamp 到 vol_max)+ `/api/audio` 补 `vol_max`)。 体积约 **256 KB**(固件阈值内)。md5:`820b24541ed69f2e27e4a301aff10c98`(UTF-8 加固:`sA_str` 统一嵌 JSON 字符串做 UTF-8 消毒,非法/截断字节逐字节回退 `?`——amsd 曾把「流媒体客户端」截成 16B 腰斩「端」字导致浏览器拒收整页,面板侧防任一上游再产坏字节;`--selftest` 宿主回归;原始 IPC JSON(perf/power/net 含 ssid)先 `utf8_valid_n` 校验,坏字节降级不拖垮 /api/overview)。
**重防火墙注意**:运行中二进制先 `.new` 再 `mv`,切勿直接覆盖运行中的 `meowpanel`)。 **重防火墙注意**:运行中二进制先 `.new` 再 `mv`,切勿直接覆盖运行中的 `meowpanel`)。
- `meowpanel_host`:aarch64 动态链接(宿主编译,仅供调试),体积约 **148 KB**。 - `meowpanel_host`:aarch64 动态链接(宿主编译,仅供调试),体积约 **148 KB**。
- 依赖:仅 libc + Linux 内核头(`linux/input.h`),零第三方库(设备无 python3/lua)。鉴权用**内联 SHA-256**(拷贝自 - 依赖:仅 libc + Linux 内核头(`linux/input.h`),零第三方库(设备无 python3/lua)。鉴权用**内联 SHA-256**(拷贝自
Binary file not shown.
+173 -38
View File
@@ -208,18 +208,101 @@ static void sA(sbuf_t *s, const char *fmt, ...)
s->b[s->n] = 0; s->b[s->n] = 0;
} }
/* JSON 字符串转义(version/hostname/文本值;UTF-8 按字节透传,控制字符转 \u00xx) */ /* ---------------- UTF-8 消毒(JSON 字符串收口) ----------------
* 面板把外部文本(amsd/svcd/audiod/net/app.json/字体/日志…)嵌进 JSON 字符串,任一上游产出非法
* UTF-8(如 amsd 曾把「流媒体客户端」截成 16B,腰斩「端」字)都会让浏览器 JSON.parse 拒收整页。
* 统一在 sA_str 内逐字节校验:合法序列透传,非法/截断序列逐字节替换为 '?'(保持字节长度)。 */
static int utf8_seq_len(unsigned char lead)
{
if (lead >= 0xC2 && lead <= 0xDF) return 2; /* C2+:规避 overlong C0/C1 */
if (lead >= 0xE0 && lead <= 0xEF) return 3; /* 3 字节(含代理区,由 seq_ok 剔除) */
if (lead >= 0xF0 && lead <= 0xF4) return 4; /* F5+ 超 U+10FFFF,剔除 */
return 0;
}
/* 校验从 p 开始的 len 字节序列是否构成合法 UTF-8 字符(len 由 utf8_seq_len 给出)。
* 调用方须保证 p+len 在可读边界内。检查续字节/overlong/代理对/超 U+10FFFF。 */
static int utf8_seq_ok(const char *p, int len)
{
unsigned char c0 = (unsigned char)p[0];
for (int k = 1; k < len; k++) {
unsigned char ck = (unsigned char)p[k];
if (ck == 0 || (ck & 0xC0) != 0x80) return 0; /* 截断/非续字节 */
}
unsigned long cp;
if (len == 2) cp = ((unsigned long)(c0 & 0x1F) << 6) | ((unsigned char)p[1] & 0x3F);
else if (len == 3) cp = ((unsigned long)(c0 & 0x0F) << 12) | (((unsigned char)p[1] & 0x3F) << 6) | ((unsigned char)p[2] & 0x3F);
else cp = ((unsigned long)(c0 & 0x07) << 18) | (((unsigned char)p[1] & 0x3F) << 12) | (((unsigned char)p[2] & 0x3F) << 6) | ((unsigned char)p[3] & 0x3F);
if (len == 2 && cp < 0x80) return 0; /* overlong(C0/C1 已由 seq_len 拒,双保险) */
if (len == 3 && cp < 0x800) return 0; /* overlong E0 80.. */
if (len == 4 && cp < 0x10000) return 0; /* overlong F0 80.. */
if (cp >= 0xD800 && cp <= 0xDFFF) return 0; /* 代理对(JS 字符串不允许孤立) */
if (cp > 0x10FFFF) return 0;
return 1;
}
/* 扫描 [s, s+n) 是否为合法 UTF-8:返回 1 合法(纯 ASCII 亦属),0 存在非法/截断字节。 */
static int utf8_valid_n(const char *s, size_t n)
{
size_t i = 0;
while (i < n) {
unsigned char c = (unsigned char)s[i];
if (c < 0x80) { i++; continue; }
int len = utf8_seq_len(c);
if (len == 0 || i + (size_t)len > n || !utf8_seq_ok(s + i, len)) return 0;
i += (size_t)len;
}
return 1;
}
/* UTF-8 消毒:将 src 前 n 字节拷贝到 dst(dst 容量 ≥ n+1,恒 NUL 结尾),非法/截断序列
* 逐字节替换为 '?'(保持字节长度)。返回 1 = 发生过替换,0 = 原样合法。无依赖,纯指针运算。 */
static int utf8_sanitize(char *dst, const char *src, size_t n)
{
size_t i = 0, j = 0;
int changed = 0;
while (i < n) {
unsigned char c = (unsigned char)src[i];
if (c < 0x80) { dst[j++] = (char)c; i++; continue; }
int len = utf8_seq_len(c);
if (len == 0 || i + (size_t)len > n || !utf8_seq_ok(src + i, len)) {
dst[j++] = '?'; changed = 1; i++; continue; /* 逐字节替换,前进 1 重对齐 */
}
memcpy(dst + j, src + i, (size_t)len);
j += (size_t)len; i += (size_t)len;
}
dst[j] = 0;
return changed;
}
/* JSON 字符串转义 + UTF-8 消毒(非 NUL 终止、长度明确版;控制字符转 \u00xx,非法 UTF-8 逐字节转 '?')。 */
static void sA_str_n(sbuf_t *s, const char *v, size_t n)
{
sA(s, "\"");
size_t i = 0;
while (i < n) {
unsigned char c = (unsigned char)v[i];
if (c == '"' || c == '\\') { sA(s, "\\%c", (int)c); i++; }
else if (c < 0x20) { sA(s, "\\u%04x", c); i++; }
else if (c < 0x80) { sA(s, "%c", (int)c); i++; }
else {
int len = utf8_seq_len(c);
if (len == 0 || i + (size_t)len > n || !utf8_seq_ok(v + i, len)) {
sA(s, "?"); i++; /* 非法/截断序列 → 逐字节替换为 '?',前进 1 重对齐 */
} else {
for (int k = 0; k < len; k++) sA(s, "%c", (int)(unsigned char)v[i + k]);
i += (size_t)len;
}
}
}
sA(s, "\"");
}
/* NUL 结尾字符串的 JSON 嵌入(统一收口:所有外部文本嵌 JSON 字符串都走这里)。 */
static void sA_str(sbuf_t *s, const char *v) static void sA_str(sbuf_t *s, const char *v)
{ {
if (!v) v = ""; if (!v) v = "";
sA(s, "\""); sA_str_n(s, v, strlen(v));
for (const char *p = v; *p; p++) {
unsigned char c = (unsigned char)*p;
if (c == '"' || c == '\\') { sA(s, "\\%c", (int)c); }
else if (c < 0x20) { sA(s, "\\u%04x", c); }
else sA(s, "%c", (int)c);
}
sA(s, "\"");
} }
/* 追加一段原始字节到 sbuf(长度明确,绕过 %s 的固定缓冲限制;用于大 JSON 数组复用)。 */ /* 追加一段原始字节到 sbuf(长度明确,绕过 %s 的固定缓冲限制;用于大 JSON 数组复用)。 */
@@ -1128,7 +1211,7 @@ static int api_power(int sock)
{ {
char js[MEOW_IPC_MAX]; char js[MEOW_IPC_MAX];
int rc = ipc_pm_query(0, js, sizeof js); int rc = ipc_pm_query(0, js, sizeof js);
if (rc != MEOW_OK) if (rc != MEOW_OK || !utf8_valid_n(js, strlen(js))) /* 上游坏字节 → 不返回,降级 */
return send_err(sock, 503, "POWER_UNAVAILABLE", "电源管理服务不可用"); return send_err(sock, 503, "POWER_UNAVAILABLE", "电源管理服务不可用");
return send_json(sock, 200, js); return send_json(sock, 200, js);
} }
@@ -1189,6 +1272,8 @@ static int api_net_status(int sock)
int n = net_ipc(MSG_NET_STATUS_REQ, NULL, 0, &t, &code, NULL, json, sizeof json); 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]) if (n <= 0 || t != MSG_NET_STATUS_RESULT || code != MEOW_OK || !json[0])
return send_err(sock, 503, "NET_UNAVAILABLE", "网络管理服务不可用"); return send_err(sock, 503, "NET_UNAVAILABLE", "网络管理服务不可用");
if (!utf8_valid_n(json, strlen(json))) /* 上游坏字节(含 ssid)→ 不返回,降级 */
return send_err(sock, 503, "NET_UNAVAILABLE", "网络管理服务应答包含非法字节");
return send_json(sock, 200, json); return send_json(sock, 200, json);
} }
@@ -1301,6 +1386,7 @@ static int net_block_fill(sbuf_t *s)
size_t l = strlen(njson); size_t l = strlen(njson);
while (l > 0 && (njson[l - 1] == ' ' || njson[l - 1] == '\t' || njson[l - 1] == '\n' || njson[l - 1] == '\r')) njson[--l] = 0; while (l > 0 && (njson[l - 1] == ' ' || njson[l - 1] == '\t' || njson[l - 1] == '\n' || njson[l - 1] == '\r')) njson[--l] = 0;
if (l < 2 || njson[0] != '{' || njson[l - 1] != '}') return -1; if (l < 2 || njson[0] != '{' || njson[l - 1] != '}') return -1;
if (!utf8_valid_n(njson, l)) return -1; /* 任一上游(netmgrd 含 ssid)产出非法 UTF-8 → 整块降级,不拖垮整页 */
njson[l - 1] = 0; /* 去尾部 } */ njson[l - 1] = 0; /* 去尾部 } */
sbuf_t dns = {0}; sbuf_t dns = {0};
int drc = net_dns_obj_fill(&dns); int drc = net_dns_obj_fill(&dns);
@@ -1393,17 +1479,8 @@ static int api_net_log(int sock)
const char *line_end = nl ? nl : end; const char *line_end = nl ? nl : end;
if (rowidx >= startrow) { if (rowidx >= startrow) {
if (gotlines) sA(&s, ","); if (gotlines) sA(&s, ",");
sbuf_t ln = {0}; /* sA_str_n:转义 + UTF-8 消毒(任意长度安全;勿 sA("%s")——单次超 511B 会截断坏 JSON) */
for (const char *c = p; c < line_end; c++) { sA_str_n(&s, p, (size_t)(line_end - p));
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++; gotlines++;
} }
rowidx++; rowidx++;
@@ -1587,10 +1664,12 @@ static int api_overview(int sock)
sbuf_t s = {0}; sbuf_t s = {0};
sA(&s, "{\"ts\":%lld,", (long long)time(NULL)); sA(&s, "{\"ts\":%lld,", (long long)time(NULL));
char pb[4096]; char pb[4096];
if (ipc_perf_query(0, pb, sizeof pb) == MEOW_OK) sA(&s, "\"perf\":%s,", pb); if (ipc_perf_query(0, pb, sizeof pb) == MEOW_OK && utf8_valid_n(pb, strlen(pb)))
sA(&s, "\"perf\":%s,", pb);
else sA(&s, "\"perf\":{\"available\":false},"); else sA(&s, "\"perf\":{\"available\":false},");
char pw[MEOW_IPC_MAX]; char pw[MEOW_IPC_MAX];
if (ipc_pm_query(0, pw, sizeof pw) == MEOW_OK) sA(&s, "\"power\":%s,", pw); if (ipc_pm_query(0, pw, sizeof pw) == MEOW_OK && utf8_valid_n(pw, strlen(pw)))
sA(&s, "\"power\":%s,", pw);
else sA(&s, "\"power\":{\"available\":false},"); else sA(&s, "\"power\":{\"available\":false},");
sA(&s, "\"audio\":"); sA(&s, "\"audio\":");
audio_fill(&s); audio_fill(&s);
@@ -3335,7 +3414,8 @@ static int api_perf(int sock, const char *path)
{ {
char json[4096]; char json[4096];
int rc = ipc_perf_query(perf_subset_for_path(path), json, sizeof json); int rc = ipc_perf_query(perf_subset_for_path(path), json, sizeof json);
if (rc != MEOW_OK) return send_err(sock, 500, "INTERNAL", "性能服务不可用(IPC 连接/应答失败)"); if (rc != MEOW_OK || !utf8_valid_n(json, strlen(json))) /* 上游坏字节 → 不返回,降级 */
return send_err(sock, 500, "INTERNAL", "性能服务不可用(IPC 连接/应答失败)");
return send_json(sock, 200, json); return send_json(sock, 200, json);
} }
@@ -3436,8 +3516,9 @@ static int api_system(int sock)
sA_str(&s, bootmode); sA_str(&s, bootmode);
sA(&s, "},\"network\":"); sA(&s, "},\"network\":");
char netbuf[1024]; char netbuf[1024];
if (perf_extract_network(json, netbuf, sizeof netbuf) == 0) { /* 透传 perf 权威 network 段(wifi/bluetooth/hostname_local);含非法 UTF-8(如坏 ssid)→ 回退,不嵌坏字节 */
sA(&s, "%s", netbuf); /* 透传 perf 权威 network 段(wifi/bluetooth/hostname_local) */ if (perf_extract_network(json, netbuf, sizeof netbuf) == 0 && utf8_valid_n(netbuf, strlen(netbuf))) {
sA(&s, "%s", netbuf);
} else { } else {
/* 回退:perf 未含 network 段 → 仅 hostname_local(保持原语义,不崩) */ /* 回退:perf 未含 network 段 → 仅 hostname_local(保持原语义,不崩) */
sA(&s, "{\"hostname_local\":"); sA(&s, "{\"hostname_local\":");
@@ -3698,18 +3779,8 @@ static int api_logs_read(int sock, const req_t *r)
const char *line_end = nl ? nl : end; const char *line_end = nl ? nl : end;
if (rowidx >= startrow) { if (rowidx >= startrow) {
if (gotlines) sA(&s, ","); if (gotlines) sA(&s, ",");
sbuf_t ln = {0}; /* sA_str_n:转义 + UTF-8 消毒(任意长度安全;勿 sA("%s")——单次超 511B 会截断坏 JSON) */
for (const char *c = p; c < line_end; c++) { sA_str_n(&s, p, (size_t)(line_end - p));
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_str(任意长度安全);不要 sA("%s")——单次超 511B 会截断坏 JSON */
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++; gotlines++;
} }
rowidx++; rowidx++;
@@ -5523,11 +5594,75 @@ static void handle_conn(int sock)
close(sock); close(sock);
} }
/* ---------------- 宿主自测(`meowpanel --selftest`) ----------------
* 复现 2026-09-02 事故:amsd 的 am list 把 App 显示名「流媒体客户端」(18B)截成 16B,
* 腰斩「端」字(U+7AEF = E7 AB AF → 只剩首字节 E7)。面板必须把这类非法 UTF-8 消毒成合法字符,
* JSON 才不会被浏览器拒收。直接喂截断串给 utf8_sanitize / sA_str / ams_fill 桩,断言:
* 1) utf8_sanitize 改写截断串、保持字节长度、产物为合法 UTF-8,且不误改完整合法名;
* 2) sA_str 嵌入 JSON 字符串后整串为合法 UTF-8、jv_parse 可完整解析;
* 3) ams_fill 桩(APP 行解析 + sA_str)产物同理可解析。
* 仅宿主 `meowpanel --selftest` 进入;设备服务循环不触发。 */
static int panel_selftest(void)
{
const char *trunc = "流媒体客户\xE7"; /* 事故里被腰斩的显示名(16B,末字节为「端」首字节) */
const char *oknat = "流媒体客户端"; /* 完整合法名(18B,回归确认不误伤) */
size_t n = strlen(trunc);
/* 1a) utf8_sanitize:截断串必被改写,长度保持,产物合法 UTF-8 */
char san[64];
if (!utf8_sanitize(san, trunc, n)) { printf("SELFTEST FAIL: utf8_sanitize 未改写截断串\n"); return 1; }
if (strlen(san) != n) { printf("SELFTEST FAIL: utf8_sanitize 长度变化 %zu->%zu\n", n, strlen(san)); return 1; }
if (!utf8_valid_n(san, strlen(san))) { printf("SELFTEST FAIL: utf8_sanitize 产物含非法字节\n"); return 1; }
/* 1b) 完整合法名不得被改写(误伤检测) */
char san2[64];
if (utf8_sanitize(san2, oknat, strlen(oknat)) != 0 || strcmp(san2, oknat) != 0) {
printf("SELFTEST FAIL: utf8_sanitize 误改合法 UTF-8\n"); return 1;
}
/* 2) sA_str 嵌入:产物合法 UTF-8 且 jv_parse 完整解析 */
sbuf_t s = {0};
sA(&s, "{\"pkg\":");
sA_str(&s, "com.example.trunc");
sA(&s, ",\"name\":");
sA_str(&s, trunc);
sA(&s, ",\"state\":");
sA_str(&s, "running");
sA(&s, "}");
if (!s.b) { printf("SELFTEST FAIL: sA_str 输出空\n"); return 1; }
if (!utf8_valid_n(s.b, s.n)) { printf("SELFTEST FAIL: sA_str 产物含非法字节\n"); sbuf_free(&s); return 1; }
jv_t *r = jv_parse(s.b, (int)s.n);
if (!r) { printf("SELFTEST FAIL: sA_str 产物 JSON 无法解析\n"); sbuf_free(&s); return 1; }
jv_t *nm = jv_obj_get(r, "name");
if (!nm || nm->type != J_STR) { printf("SELFTEST FAIL: sA_str 产物缺 name\n"); jv_free(r); sbuf_free(&s); return 1; }
jv_free(r);
sbuf_free(&s);
/* 3) ams_fill 桩:模拟 APP 行 parse 出的截断名(join_tokens 后)→ apps 数组可解析 */
sbuf_t a = {0};
sA(&a, "{\"available\":true,\"count\":1,\"apps\":[{\"pkg\":");
sA_str(&a, "com.example.trunc");
sA(&a, ",\"name\":");
sA_str(&a, trunc);
sA(&a, ",\"state\":");
sA_str(&a, "running");
sA(&a, ",\"pid\":123}]}");
if (!a.b || !utf8_valid_n(a.b, a.n)) { printf("SELFTEST FAIL: ams_fill 桩产物含非法字节/为空\n"); sbuf_free(&a); return 1; }
jv_t *ar = jv_parse(a.b, (int)a.n);
if (!ar) { printf("SELFTEST FAIL: ams_fill 桩产物 JSON 无法解析\n"); sbuf_free(&a); return 1; }
jv_free(ar);
sbuf_free(&a);
printf("SELFTEST PASS: 截断 UTF-8「流媒体客户\\xE7」→ 合法 JSON + 无非法字节\n");
return 0;
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
/* 参数:-p PORT(可选)。默认端口 8080。 */ /* 参数:-p PORT(可选)。默认端口 8080。 */
int port = DEFAULT_PORT; int port = DEFAULT_PORT;
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i], "--selftest")) return panel_selftest();
if (!strcmp(argv[i], "-p") && i + 1 < argc) port = atoi(argv[++i]); if (!strcmp(argv[i], "-p") && i + 1 < argc) port = atoi(argv[++i]);
else if (!strcmp(argv[i], "--sock-perf") && i + 1 < argc) g_sock_perf = argv[++i]; else if (!strcmp(argv[i], "--sock-perf") && i + 1 < argc) g_sock_perf = argv[++i];
else if (!strcmp(argv[i], "--sock-power") && i + 1 < argc) g_sock_power = argv[++i]; else if (!strcmp(argv[i], "--sock-power") && i + 1 < argc) g_sock_power = argv[++i];
Binary file not shown.