已合并
fix(init/ueventd): PID 1 稳定性修复 — waitpid 超时(高-5/6) + ueventd NULL 守卫(高-10) + 单测 #4960
chenjinxiang3创建于 5 天前
fix(init/ueventd): PID 1 稳定性修复 — waitpid 超时(高-5/6) + ueventd NULL 守卫(高-10) + 单测 #4960
已合并
共 9 个文件变更+137-23
| @@ -214,6 +214,8 @@ void *OH_ExtendableStrDictGet(void **strDict, int dictSize, const char *target, | |||
| 214 | 214 | ||
| 215 | long long GetUptimeInMicroSeconds(const struct timespec *uptime); | 215 | long long GetUptimeInMicroSeconds(const struct timespec *uptime); |
| 216 | 216 | ||
| 217 | +INIT_LOCAL_API int WaitPidTimeout(pid_t pid, int timeoutMs); | ||
| 218 | + | ||
| 217 | 219 | ||
| 218 | 220 | ||
| 219 | } | 221 | } |
| @@ -23,6 +23,7 @@ | |||
| 23 | 23 | ||
| 24 | 24 | ||
| 25 | 25 | ||
| 26 | + | ||
| 26 | 27 | ||
| 27 | 28 | ||
| 28 | 29 | ||
| @@ -128,6 +129,7 @@ void PluginExecCmdByCmdIndex(int index, const char *cmdContent, const ConfigCont | |||
| 128 | const char *PluginGetCmdIndex(const char *cmdStr, int *index); | 129 | const char *PluginGetCmdIndex(const char *cmdStr, int *index); |
| 129 | const char *GetPluginCmdNameByIndex(int index); | 130 | const char *GetPluginCmdNameByIndex(int index); |
| 130 | int AddCareContextCmdExecutor(const char *cmdName, CmdExecutor executor); | 131 | int AddCareContextCmdExecutor(const char *cmdName, CmdExecutor executor); |
| 132 | +INIT_LOCAL_API int SyncExecCommand(int argc, char * const *argv); | ||
| 131 | 133 | ||
| 132 | 134 | ||
| 133 | 135 | ||
| @@ -1447,13 +1447,17 @@ static void StopWorkingset(const char *workingsetPath) | |||
| 1447 | close(workingsetFd); | 1447 | close(workingsetFd); |
| 1448 | } | 1448 | } |
| 1449 | 1449 | ||
| 1450 | + | ||
| 1451 | + | ||
| 1452 | + | ||
| 1453 | + | ||
| 1450 | static void StopAppSpawnBeforeReboot(void) | 1454 | static void StopAppSpawnBeforeReboot(void) |
| 1451 | { | 1455 | { |
| 1452 | Service *service = GetServiceByName("appspawn"); | 1456 | Service *service = GetServiceByName("appspawn"); |
| 1453 | if (service != NULL && service->pid > 0) { // notify appspawn stop | 1457 | if (service != NULL && service->pid > 0) { // notify appspawn stop |
| 1454 | 1458 | ||
| 1455 | kill(service->pid, SIGTERM); | 1459 | kill(service->pid, SIGTERM); |
| 1456 | - waitpid(service->pid, 0, 0); | 1460 | + WaitPidTimeout(service->pid, APPSPAWN_WAIT_TIMEOUT_MS); |
| 1457 | service->pid = -1; | 1461 | service->pid = -1; |
| 1458 | 1462 | ||
| 1459 | } | 1463 | } |
| @@ -152,7 +152,15 @@ int GetParamValue(const char *symValue, unsigned int symLen, char *paramValue, u | |||
| 152 | return 0; | 152 | return 0; |
| 153 | } | 153 | } |
| 154 | 154 | ||
| 155 | -static int SyncExecCommand(int argc, char * const *argv) | 155 | +#ifdef STARTUP_INIT_TEST |
| 156 | + | ||
| 157 | + | ||
| 158 | + | ||
| 159 | + | ||
| 160 | + | ||
| 161 | + | ||
| 162 | + | ||
| 163 | +int SyncExecCommand(int argc, char * const *argv) | ||
| 156 | { | 164 | { |
| 157 | INIT_LOGI("Sync exec: %s", argv[0]); | 165 | INIT_LOGI("Sync exec: %s", argv[0]); |
| 158 | pid_t pid = fork(); | 166 | pid_t pid = fork(); |
| @@ -161,9 +169,8 @@ static int SyncExecCommand(int argc, char * const *argv) | |||
| 161 | INIT_CHECK_ONLY_ELOG(execv(argv[0], argv) == 0, "execv %s failed! err %d.", argv[0], errno); | 169 | INIT_CHECK_ONLY_ELOG(execv(argv[0], argv) == 0, "execv %s failed! err %d.", argv[0], errno); |
| 162 | exit(-1); | 170 | exit(-1); |
| 163 | } | 171 | } |
| 164 | - int status; | 172 | + int status = WaitPidTimeout(pid, SYNC_EXEC_TIMEOUT_MS); |
| 165 | - pid_t ret = waitpid(pid, &status, 0); | 173 | + if (status < 0) { |
| 166 | - if (ret != pid) { | ||
| 167 | INIT_LOGE("failed wait pid %d, errno %d", pid, errno); | 174 | INIT_LOGE("failed wait pid %d, errno %d", pid, errno); |
| 168 | return -1; | 175 | return -1; |
| 169 | } | 176 | } |
| @@ -821,9 +828,8 @@ static void DumpFdInfo() | |||
| 821 | INIT_LOGE("fork failed, err is %d", errno); | 828 | INIT_LOGE("fork failed, err is %d", errno); |
| 822 | return; | 829 | return; |
| 823 | } | 830 | } |
| 824 | - int status; | 831 | + int status = WaitPidTimeout(pid, FD_DUMP_TIMEOUT_MS); |
| 825 | - pid_t ret = waitpid(pid, &status, 0); | 832 | + if (status < 0) { |
| 826 | - if (ret != pid) { | ||
| 827 | INIT_LOGE("failed wait pid %d, errno is %d", pid, errno); | 833 | INIT_LOGE("failed wait pid %d, errno is %d", pid, errno); |
| 828 | } | 834 | } |
| 829 | INIT_LOGI("dump fd info end"); | 835 | INIT_LOGI("dump fd info end"); |
| @@ -21,10 +21,12 @@ | |||
| 21 | 21 | ||
| 22 | 22 | ||
| 23 | 23 | ||
| 24 | + | ||
| 24 | 25 | ||
| 25 | 26 | ||
| 26 | 27 | ||
| 27 | 28 | ||
| 29 | + | ||
| 28 | 30 | ||
| 29 | 31 | ||
| 30 | 32 | ||
| @@ -951,3 +953,25 @@ long long GetUptimeInMicroSeconds(const struct timespec *uptime) | |||
| 951 | return ((long long)uptime->tv_sec * SECOND_TO_MICRO_SECOND) + | 953 | return ((long long)uptime->tv_sec * SECOND_TO_MICRO_SECOND) + |
| 952 | (uptime->tv_nsec / MICRO_SECOND_TO_NANOSECOND); | 954 | (uptime->tv_nsec / MICRO_SECOND_TO_NANOSECOND); |
| 953 | } | 955 | } |
| 956 | + | ||
| 957 | + | ||
| 958 | + | ||
| 959 | +int WaitPidTimeout(pid_t pid, int timeoutMs) | ||
| 960 | +{ | ||
| 961 | + int status = 0; | ||
| 962 | + int retry = timeoutMs / WAIT_POLL_INTERVAL_MS; | ||
| 963 | + while (retry-- > 0) { | ||
| 964 | + pid_t r = waitpid(pid, &status, WNOHANG); | ||
| 965 | + if (r == pid) { | ||
| 966 | + return status; | ||
| 967 | + } | ||
| 968 | + if (r < 0) { | ||
| 969 | + return -1; | ||
| 970 | + } | ||
| 971 | + usleep(WAIT_POLL_INTERVAL_US); | ||
| 972 | + } | ||
| 973 | + INIT_LOGE("wait pid %d timeout, force kill", pid); | ||
| 974 | + kill(pid, SIGKILL); | ||
| 975 | + waitpid(pid, &status, 0); | ||
| 976 | + return -1; | ||
| 977 | +} | ||
| @@ -15,10 +15,14 @@ | |||
| 15 | 15 | ||
| 16 | 16 | ||
| 17 | 17 | ||
| 18 | + | ||
| 18 | 19 | ||
| 19 | 20 | ||
| 20 | 21 | ||
| 21 | 22 | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 22 | 26 | ||
| 23 | 27 | ||
| 24 | 28 | ||
| @@ -617,4 +621,43 @@ HWTEST_F(CmdsUnitTest, TestDeInitEswapSpace, TestSize.Level1) | |||
| 617 | EXPECT_EQ(ret, true); | 621 | EXPECT_EQ(ret, true); |
| 618 | } | 622 | } |
| 619 | } | 623 | } |
| 624 | + | ||
| 625 | +// Branch 1: child exits quickly → returns the child's exit status. | ||
| 626 | +HWTEST_F(CmdsUnitTest, WaitPidTimeout_ChildExits, TestSize.Level1) | ||
| 627 | +{ | ||
| 628 | + constexpr int testExitCode = 42; | ||
| 629 | + constexpr int testWaitTimeoutMs = 3000; | ||
| 630 | + pid_t pid = fork(); | ||
| 631 | + ASSERT_GE(pid, 0); | ||
| 632 | + if (pid == 0) { | ||
| 633 | + _exit(testExitCode); | ||
| 634 | + } | ||
| 635 | + int status = WaitPidTimeout(pid, testWaitTimeoutMs); | ||
| 636 | + EXPECT_NE(status, -1); | ||
| 637 | + EXPECT_EQ(WEXITSTATUS(status), testExitCode); | ||
| 638 | +} | ||
| 639 | + | ||
| 640 | +// Branch 2: invalid pid → waitpid returns -1 → returns -1 immediately. | ||
| 641 | +HWTEST_F(CmdsUnitTest, WaitPidTimeout_InvalidPid, TestSize.Level1) | ||
| 642 | +{ | ||
| 643 | + constexpr pid_t invalidTestPid = 999999; | ||
| 644 | + constexpr int testShortTimeoutMs = 1000; | ||
| 645 | + int status = WaitPidTimeout(invalidTestPid, testShortTimeoutMs); | ||
| 646 | + EXPECT_EQ(status, -1); | ||
| 647 | +} | ||
| 648 | + | ||
| 649 | +// Branch 3: child hangs → timeout → SIGKILL + reap → returns -1. | ||
| 650 | +HWTEST_F(CmdsUnitTest, WaitPidTimeout_TimeoutKill, TestSize.Level1) | ||
| 651 | +{ | ||
| 652 | + constexpr int testKillTimeoutMs = 300; | ||
| 653 | + pid_t pid = fork(); | ||
| 654 | + ASSERT_GE(pid, 0); | ||
| 655 | + if (pid == 0) { | ||
| 656 | + for (;;) {} | ||
| 657 | + } | ||
| 658 | + int status = WaitPidTimeout(pid, testKillTimeoutMs); | ||
| 659 | + EXPECT_EQ(status, -1); | ||
| 660 | + EXPECT_EQ(kill(pid, 0), -1); | ||
| 661 | + EXPECT_EQ(errno, ESRCH); | ||
| 662 | +} | ||
| 620 | } // namespace init_ut | 663 | } // namespace init_ut |
| @@ -787,4 +787,28 @@ HWTEST_F(UeventdEventUnitTest, Init_BootDeviceIsMatchedTest_DefaultBootDevice001 | |||
| 787 | EXPECT_EQ(ret, 0); | 787 | EXPECT_EQ(ret, 0); |
| 788 | } | 788 | } |
| 789 | 789 | ||
| 790 | +// Branch 1: loop matches a required name → return true. | ||
| 791 | +HWTEST_F(UeventdEventUnitTest, IsRequiredPartitionName_Match, TestSize.Level1) | ||
| 792 | +{ | ||
| 793 | + EXPECT_TRUE(IsRequiredPartitionName("vendor")); | ||
| 794 | + EXPECT_TRUE(IsRequiredPartitionName("system")); | ||
| 795 | + EXPECT_TRUE(IsRequiredPartitionName("ramdisk")); | ||
| 796 | + EXPECT_TRUE(IsRequiredPartitionName("myvendor")); | ||
| 797 | +} | ||
| 798 | + | ||
| 799 | +// Branch 2: loop exhausts, IsOtherPartitionName returns true → return true. | ||
| 800 | +HWTEST_F(UeventdEventUnitTest, IsRequiredPartitionName_OtherMatch, TestSize.Level1) | ||
| 801 | +{ | ||
| 802 | + EXPECT_TRUE(IsRequiredPartitionName("patch_a")); | ||
| 803 | + EXPECT_TRUE(IsRequiredPartitionName("version_b")); | ||
| 804 | + EXPECT_TRUE(IsRequiredPartitionName("cust_a")); | ||
| 805 | +} | ||
| 806 | + | ||
| 807 | +// Branch 3: neither in requiredNames nor IsOtherPartitionName → return false. | ||
| 808 | +HWTEST_F(UeventdEventUnitTest, IsRequiredPartitionName_NoMatch, TestSize.Level1) | ||
| 809 | +{ | ||
| 810 | + EXPECT_FALSE(IsRequiredPartitionName("unknownpart")); | ||
| 811 | + EXPECT_FALSE(IsRequiredPartitionName("xyz")); | ||
| 812 | +} | ||
| 813 | + | ||
| 790 | } // UeventdUt | 814 | } // UeventdUt |
| @@ -17,6 +17,7 @@ | |||
| 17 | 17 | ||
| 18 | 18 | ||
| 19 | 19 | ||
| 20 | + | ||
| 20 | 21 | ||
| 21 | 22 | ||
| 22 | extern "C" { | 23 | extern "C" { |
| @@ -85,6 +86,7 @@ void ProcessUevent(int sockFd, char **devices, int num, CompareUevent compare); | |||
| 85 | void CloseUeventConfig(void); | 86 | void CloseUeventConfig(void); |
| 86 | char **GetBootDeviceArray(void); | 87 | char **GetBootDeviceArray(void); |
| 87 | int GetBootDeviceNum(void); | 88 | int GetBootDeviceNum(void); |
| 89 | +INIT_LOCAL_API bool IsRequiredPartitionName(const char *partitionName); | ||
| 88 | 90 | ||
| 89 | 91 | ||
| 90 | } | 92 | } |
| @@ -254,28 +254,34 @@ static void SaveDataUeventInfo(const struct Uevent *uevent) | |||
| 254 | } | 254 | } |
| 255 | 255 | ||
| 256 | 256 | ||
| 257 | +bool IsRequiredPartitionName(const char *partitionName) | ||
| 258 | +{ | ||
| 259 | + static const char *requiredNames[] = { | ||
| 260 | + "vendor", "system", "sys_prod", "chip_prod", "chipset", "boot", | ||
| 261 | + "ramdisk", "rvt", "dtbo", "modem_driver", "hyperhold" | ||
| 262 | + }; | ||
| 263 | + const size_t count = sizeof(requiredNames) / sizeof(requiredNames[0]); | ||
| 264 | + for (size_t i = 0; i < count; i++) { | ||
| 265 | + if (strstr(partitionName, requiredNames[i]) != NULL) { | ||
| 266 | + return true; | ||
| 267 | + } | ||
| 268 | + } | ||
| 269 | + return IsOtherPartitionName(partitionName); | ||
| 270 | +} | ||
| 271 | + | ||
| 257 | static void HandleRequiredBlockDeviceNodes(const struct Uevent *uevent, char **devices, int num) | 272 | static void HandleRequiredBlockDeviceNodes(const struct Uevent *uevent, char **devices, int num) |
| 258 | { | 273 | { |
| 259 | for (int i = 0; i < num; i++) { | 274 | for (int i = 0; i < num; i++) { |
| 260 | if (uevent->partitionName == NULL) { | 275 | if (uevent->partitionName == NULL) { |
| 261 | - if (strstr(devices[i], uevent->deviceName) != NULL) { | 276 | + if (uevent->deviceName != NULL && |
| 262 | - INIT_LOGI("%s match with required partition %s success, now handle it", devices[i], uevent->deviceName); | 277 | + strstr(devices[i], uevent->deviceName) != NULL) { |
| 278 | + INIT_LOGI("%s match with required partition %s success, now handle it", | ||
| 279 | + devices[i], uevent->deviceName); | ||
| 263 | HandleBlockDeviceEvent(uevent); | 280 | HandleBlockDeviceEvent(uevent); |
| 264 | return; | 281 | return; |
| 265 | } | 282 | } |
| 266 | } else if (strstr(devices[i], uevent->partitionName) != NULL || | 283 | } else if (strstr(devices[i], uevent->partitionName) != NULL || |
| 267 | - strstr(uevent->partitionName, "vendor") != NULL || | 284 | + IsRequiredPartitionName(uevent->partitionName)) { |
| 268 | - strstr(uevent->partitionName, "system") != NULL || | ||
| 269 | - strstr(uevent->partitionName, "sys_prod") != NULL || | ||
| 270 | - strstr(uevent->partitionName, "chip_prod") != NULL || | ||
| 271 | - strstr(uevent->partitionName, "chipset") != NULL || | ||
| 272 | - strstr(uevent->partitionName, "boot") != NULL || | ||
| 273 | - strstr(uevent->partitionName, "ramdisk") != NULL || | ||
| 274 | - strstr(uevent->partitionName, "rvt") != NULL || | ||
| 275 | - strstr(uevent->partitionName, "dtbo") != NULL || | ||
| 276 | - strstr(uevent->partitionName, "modem_driver") != NULL || | ||
| 277 | - strstr(uevent->partitionName, "hyperhold") != NULL || | ||
| 278 | - IsOtherPartitionName(uevent->partitionName)) { | ||
| 279 | INIT_LOGI("Handle required partitionName %s", uevent->partitionName); | 285 | INIT_LOGI("Handle required partitionName %s", uevent->partitionName); |
| 280 | HandleBlockDeviceEvent(uevent); | 286 | HandleBlockDeviceEvent(uevent); |
| 281 | return; | 287 | return; |
| @@ -284,7 +290,8 @@ static void HandleRequiredBlockDeviceNodes(const struct Uevent *uevent, char **d | |||
| 284 | return; | 290 | return; |
| 285 | } | 291 | } |
| 286 | } | 292 | } |
| 287 | - INIT_LOGW("Not found device for partitionName %s ", uevent->partitionName); | 293 | + INIT_LOGW("Not found device for partitionName %s ", |
| 294 | + (uevent->partitionName != NULL) ? uevent->partitionName : "(null)"); | ||
| 288 | } | 295 | } |
| 289 | 296 | ||
| 290 | static void HandleUeventRequired(const struct Uevent *uevent, char **devices, int num) | 297 | static void HandleUeventRequired(const struct Uevent *uevent, char **devices, int num) |