现象
执行网关后端程序,发现ip不显示,ifconfig也不显示ip:
ifconfig enx62fde47ffdbb
enx62fde47ffdbb: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
ether 62:fd:e4:7f:fd:bb txqueuelen 1000 (Ethernet)
RX packets 150 bytes 11956 (11.9 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 87 bytes 7754 (7.7 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
但是模组已经拿到ip了
+SPCHMDATAINFO:1,1,sipa_eth0,1,172.170.11.114/16,8.8.8.8 8.8.4.4,,1400,1
notification:
^NDISDUN:sipa_usb0,1,enabled,IP,172.170.11.114,255.255.255.255,172.170.11.114,8.8.8.8,8.8.4.4
解决
用gdb检查后端程序,发现问题:mypopen执行失败。
string UsbnameGet() {
string res;
// string cmd = "dmesg | grep 'CDC NCM' | grep 'register' | awk '{print $5}' | cut -d: -f1"; // 这个获取的是驱动刚注册时获取的名字
// 获取到的就是实际在用的接口名:
string cmd = "for i in /sys/class/net/*; do "
"readlink $i/device/driver 2>/dev/null | grep -q cdc_ncm && basename $i; "
"done";
if(!myPopen(cmd, res)){
hloge("check 5G net card failed");
return "";
}
std::cout<<"5G网卡:"<<res<<std::endl;
return res;
}
可能是指令没权限,可能mypopen有问题,先逐步排查从最有可能的地方入手:
1.修改mypopen为popen
ifconfig正常显示ip,说明确实是mypopen有问题
std::string UsbnameGet()
{
std::string res;
const char* cmd =
"for i in /sys/class/net/*; do "
"drv=$(readlink $i/device/driver 2>/dev/null); "
"echo $drv | grep -Eq 'cdc_ncm|cdc_ether|rndis_host' && basename $i; "
"done";
FILE* fp = popen(cmd, "r");
if (!fp) {
perror("popen failed");
return "";
}
char buf[256];
while (fgets(buf, sizeof(buf), fp)) {
res += buf;
}
int rc = pclose(fp);
std::cout << "pclose rc=" << rc << std::endl;
std::cout << "RAW res=[" << res << "] len=" << res.size() << std::endl;
return res;
}
ifconfig enx62fde47ffdbb
enx62fde47ffdbb: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 172.170.11.114 netmask 255.255.255.255 broadcast 0.0.0.0
inet6 fe80::a66d:3382:f0a2:fd41 prefixlen 64 scopeid 0x20<link>
ether 62:fd:e4:7f:fd:bb txqueuelen 1000 (Ethernet)
RX packets 159 bytes 13476 (13.4 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 119 bytes 12114 (12.1 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
mypopen代码如下:
cpp
static bool executeChild(const string &cmd, string &res) {
FILE *fp = popen(cmd.c_str(),"r");
if(NULL == fp) {
// if fork(2) or pipe(2) calls fail, or cannot callocate memory.
// it does not set errno if memory allocation fails.
// if the underlying fork(2) or pipe(2) fails, errno is set
// appropriately.
// if the type arguments is invalid, and this condition is detected,
// errno is set to EINVAL.
res += static_cast<string>("popen failed. ") + strerror(errno);
return false;
}
char buffer[512] = {0};
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
res += buffer;
}
int status = pclose(fp); //❗❗❗❗❗❗❗逻辑有严重问题❗❗❗❗❗❗❗❗❗❗
if (status == 0) {
return true;
}
if (status == -1) {
res += static_cast<string>(" failed to get child status: ") + strerror(errno);
} else {
if (WIFEXITED(status)) {
// 命令不存在,exit_status = 127; 命令不存在或无权限执行,仍然是执行成功,只是exit_status>0
res += " normal termination, exit_status = " + to_string(WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
res += " termination by a signal, signal number = " + to_string(WTERMSIG(status));
} else if (WIFSTOPPED(status)) {
res += " the child is stopped, status = " + to_string(WSTOPSIG(status));
} else {
res += static_cast<string>(" unknown error: ") + strerror(errno);
}
}
return false;
}
bool myPopen(string cmd, string &res, int maxTimes) {
if (cmd.empty()) {
hloge("myPopen cmd null");
return false;
}
cmd += " 2>&1";
for(int tryTimes = 0; tryTimes < maxTimes; tryTimes++) {
bool success = executeChild(cmd, res); // ❗❗❗❗严重问题❗❗❗❗❗
if (success) {
res = hv::rtrim(res);
return true;
}
hlogi("mySystem cmd: %s, err msg: %s, try times: %d", cmd.c_str(), res.c_str(), tryTimes+1);
res.clear();
}
hloge("mySystem cmd: %s, failed", cmd.c_str());
return false;
}
bool mySystem(string cmd) {
string res;
return myPopen(cmd, res);
}
我的指令是:
cpp
for i in /sys/class/net/*; do
drv=$(readlink $i/device/driver 2>/dev/null)
echo $drv | grep -Eq 'cdc_ncm|cdc_ether|rndis_host' && basename $i
done
grep -q:匹配到,退出码0,没匹配到,退出码1.
for循环里,只要某次grep -q没匹配,最后一个退出码可能是1
但stdout里早就已经输出了正确的enx62fde47ffdbb\n
这在shell里是完全成功的,但在executeChild() 里被当成"失败"
还把stderr 合并进来了,雪上加霜:
cmd += " 2>&1";
stderr 的提示,shell 的调试信息,都被拼进 res
然后又在失败分支里继续拼:
res += " normal termination, exit_status = X";
res 被污染
更不可能"等于期望的接口名"
修改:
cpp
static bool executeChild(const string &cmd, string &res) {
FILE *fp = popen(cmd.c_str(),"r");
if (!fp) {
res += string("popen failed: ") + strerror(errno);
return false;
}
char buffer[512];
while (fgets(buffer, sizeof(buffer), fp)) {
res += buffer;
}
int status = pclose(fp);
// 仅用于日志
if (status != 0) {
if (WIFEXITED(status)) {
hlogi("cmd exit_status=%d", WEXITSTATUS(status));
}
}
// ✅ 不用 exit code 决定成败
return !res.empty();
}
问题
按照错误码本身没什么问题呀?
for 循环里最后一次命令的退出码 ≠ 你想表达的"整体成功/失败"
-
for 循环 + grep -q → 非常不适合用 exit code 判断成功
-
cmd 是"语义正确的",但"退出码语义不可靠"
这个是经典的shell坑,shell的语义本身就不是想象中的"整体成功或失败"
我的指令:
cpp
for i in /sys/class/net/*; do
drv=$(readlink $i/device/driver 2>/dev/null)
echo $drv | grep -Eq 'cdc_ncm|cdc_ether|rndis_host' && basename $i
done
shell 的 退出码规则只有一句话:
整个 for 循环的退出码 = 最后一次执行的那条"简单命令"的退出码
不是:
-
有没有输出
-
有没有"某一次成功"
-
有没有
basename打印
而是:
最后一个 grep -Eq 的返回值
系统网卡顺序是:
cpp
lo
enP5p1s0f0
enP5p1s0f1
...
enx62fde47ffdbb ← 这是 cdc_ncm
usb0
usb1 ← 最后一个
执行过程是:
1️⃣ 前面一堆接口
→ grep -Eq 不匹配
→ exit code = 1
2️⃣ 执行到 enx62fde47ffdbb
→ grep -Eq 匹配成功
→ basename 被执行
→ stdout 正确输出 enx62fde47ffdbb
3️⃣ 接着继续循环(for 不会停)
4️⃣ 最后一个接口(比如 usb1)
→ grep -Eq 不匹配
→ exit code = 1
最终结果(致命点)
-
stdout:有我想要的内容
enx62fde47ffdbb -
shell 最终退出码:1
这在shell语义里是完全合理的,但是被executeChild当成失败
所以:
不要用 exit code 判命令
C++里已经有:
cpp
res += buffer;
正确的工程判断是:
| 判断项 | 适合吗 |
|---|---|
| popen 是否成功 | ✅ |
| stdout 是否非空 | ✅ |
| shell exit code | ❌(在这个场景) |
修改后崩溃!
由于把通用执行器改了,影响了全局:
「exit code == 0」改成了「stdout 非空」
命令实际上成功了,但被当成失败,后续流程被中断。
程序崩溃没有ip,所以只能改回原来的通用执行器,修改UsbnameGet(),使其忽略返回值或者用popen代替。如果没有匹配到,也是返回空。
std::string UsbnameGet()
{
std::string res;
const char* cmd =
"for i in /sys/class/net/*; do "
"drv=$(readlink $i/device/driver 2>/dev/null); "
"echo $drv | grep -Eq 'cdc_ncm|cdc_ether|rndis_host' && basename $i; "
"done";
FILE* fp = popen(cmd, "r");
if (!fp) {
perror("popen failed");
return "";
}
char buf[256];
while (fgets(buf, sizeof(buf), fp)) {
res += buf;
}
int rc = pclose(fp);
std::cout << "pclose rc=" << rc << std::endl;
std::cout << "RAW res=[" << res << "] len=" << res.size() << std::endl;
return res;
}