#include "precompile.h" #include "app.h" #include "osutil.h" #include "version.h" #include "memtrace.h" #include "sp_def.h" #ifdef RVC_OS_WIN #include #include #include #else #include #endif //RVC_OS_WIN #include "fileutil.h" #include "iniutil.h" #include "getopt.h" #include "toolkit.h" #include "SimpleString.h" #include "md5file.h" #include "SpBase.h" #include "SpComm.hpp" #ifdef RVC_OS_WIN #include #pragma comment(lib, "dbghelp.lib") #else #include #include #endif //RVC_OS_WIN #include #include #include #include "libtoolkit/log.h" #include #include "SpBase.h" #include "sp_dbg_export.h" #include "versionBase.h" #include "base64File.hpp" #include "RVCEventCode.h" #include "dbgutil.h" #include #include #include #include #include #ifdef realloc #undef realloc //(p, s) #endif using namespace std; #include #ifndef RVC_OS_WIN static int GetParentProcessID() { #if 0 return getppid(); #else return (int)syscall(SYS_getppid); #endif } #endif //NOT _WIN32 #ifdef RVC_OS_WIN static char spshell_execute_name[] = "SpShell.exe"; static char sphost_execute_name[] = "SpHost.exe"; static char guardian_execute_name[] = "Guardian.exe"; static char cefclient_execute_name[] = "cefclient.exe"; #else static char spshell_execute_name[] = "spshell"; static char sphost_execute_name[] = "sphost"; static char guardian_execute_name[] = "guardian"; static char cefclient_execute_name[] = "cefclient"; #endif //_WIN32 #define RESTART_MODE_DEFAULT 0 #define RESTART_MODE_NORMAL 1 #define RESTART_MODE_PRE 2 #define RESTART_MODE_SHUTDOWN 3 #define RESTART_MODE_POWEROFF 4 #define RESTART_MODE_REBOOT 5 #define DRIVER_NAME "HelloDDK" #define DRIVER_PATH "InterceptDll.sys" #define SET_EVENT \ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) #define GET_SHARE_ADD \ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS) HANDLE g_hEvent = NULL; bool g_bMD5Exist = false; bool g_bWow64 = false; CSimpleStringA g_strMD5ListPath; static int g_restartMode = RESTART_MODE_DEFAULT; static bool g_immediately = false; char* relate_processes[] = { spshell_execute_name, sphost_execute_name, cefclient_execute_name }; const int relate_processes_count = 3; char* relate_processes_ex[] = { spshell_execute_name, sphost_execute_name, guardian_execute_name, cefclient_execute_name }; const int relate_processes_ex_count = 4; const int MAX_LEN = 256; //E:\Run\version\3.10.0.1\dep;E:\Run\version\3.10.0.1\bin;E:\Run\version\3.10.0.1\dev;E:\Run\version\3.10.0.1\imdep static bool IsSpPathType(const CSimpleStringA& value, const char* key) { const int leastLength = strlen("Run//1.0.0.0/") + strlen(key); CSimpleStringA path(value.GetData()); if (path.IsNullOrEmpty() || path.GetLength() < leastLength) return false; for (int i = 0; i < path.GetLength(); ++i) { if (path[i] >= 'A' && path[i] <= 'Z') path[i] = path[i] + ('a' - 'A'); } const int sionPos = path.IndexOf(key); const int verPos = sionPos + strlen(key) + strlen(SPLIT_SLASH_STR); if (sionPos < 0) return false; int suffixPos = -1, i; int dotCnt = 0; bool lastIsNum = false; for (i = verPos; i < path.GetLength() && (path[i] >= '0' && path[i] <= '9' || path[i] == '.'); ++i) { lastIsNum = !(path[i] == '.'); if (!lastIsNum) dotCnt++; } if (i >= path.GetLength() || dotCnt != 3 || !lastIsNum) return false; return true; } static CSimpleStringA CutSpPathFromPathValue(const char* value, const char* prefix) { CSimpleStringA path(value); CSimpleStringA result(true); CAutoArray values = path.Split(ENV_SEP_CHAR); if (values.GetCount() <= 0) { return path; } for (int i = 0; i < values.GetCount(); ++i) { if (!values[i].IsStartWith(prefix) && !IsSpPathType(values[i], "version") &&! IsSpPathType(values[i], "dep")) { if (!result.IsNullOrEmpty()) result += ENV_SEP_STR; result += values[i]; DbgWithLink(LOG_LEVEL_DEBUG, LOG_TYPE_SYSTEM)("toadd:%s", values[i].GetData()); } } //if (!result.IsNullOrEmpty() && path[path.GetLength() - 1] == ENV_SEP_CHAR) { // result += ENV_SEP_STR; //} return result; } static void ResetEnvPath() { char path[MAX_PATH]; char *buf; DWORD size; #if defined(_MSC_VER) const char* name = "PATH"; #else const char* name = "LD_LIBRARY_PATH"; #endif //_MSC_VER // set current path GetModuleFileNameA(NULL, path, MAX_PATH); *strrchr(path, SPLIT_SLASH) = 0; //bin *strrchr(path, SPLIT_SLASH) = 0; //1.2.3.4 SetCurrentDirectoryA(path); size = GetEnvironmentVariableA(name, NULL, 0); buf = (char*)malloc(size+MAX_PATH*3); size = GetEnvironmentVariableA(name, buf, size); DbgWithLink(LOG_LEVEL_DEBUG, LOG_TYPE_SYSTEM)("origin path:%s", buf); CSimpleStringA newValue = CutSpPathFromPathValue(buf, path); memset(buf, 0, size + MAX_PATH * 3); size = newValue.GetLength(); if (!newValue.IsNullOrEmpty()) { strcpy(buf, newValue.GetData()); strcat(buf, ENV_SEP_STR); } // append bin sub dir to %PATH% strcat(path, SPLIT_SLASH_STR "bin"); strcat(buf, path); DbgWithLink(LOG_LEVEL_DEBUG, LOG_TYPE_SYSTEM)("nwe path:%s", buf); SetEnvironmentVariableA(name, buf); free(buf); SetEnvironmentVariableA("ModuleName", "SpShell"); } #ifdef RVC_OS_WIN static void SetWorkingSet() { SIZE_T dwMinSize, dwMaxSize; HANDLE hCurrProcess = GetCurrentProcess(); GetProcessWorkingSetSize(hCurrProcess, &dwMinSize, &dwMaxSize); if (dwMaxSize < (2 << 20)) dwMaxSize = 2 << 20; SetProcessWorkingSetSize(hCurrProcess, dwMinSize, dwMaxSize); } static LONG WINAPI SuppressError(struct _EXCEPTION_POINTERS* ExceptionInfo) { static bool hasCall = false; if (hasCall) ExitProcess(Error_Exception); else hasCall = true; char tmp[MAX_PATH]; HANDLE hDumpFile; std::string dmpFileStr; GetModuleFileNameA(NULL, tmp, MAX_PATH); *strrchr(tmp, SPLIT_SLASH) = 0; *strrchr(tmp, SPLIT_SLASH) = 0; *strrchr(tmp, SPLIT_SLASH) = 0; *strrchr(tmp, SPLIT_SLASH) = 0; *strrchr(tmp, SPLIT_SLASH) = 0; wsprintfA(tmp, "%s\\rvc\\dmp\\expt.spshell.%d.%s.dmp", tmp, GetCurrentProcessId(), STRFILEVER); hDumpFile = CreateFileA( tmp, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); if( ( hDumpFile != NULL ) && ( hDumpFile != INVALID_HANDLE_VALUE ) ) { MINIDUMP_EXCEPTION_INFORMATION mdei; MINIDUMP_TYPE mdt; mdei.ThreadId = GetCurrentThreadId(); mdei.ExceptionPointers = ExceptionInfo; mdei.ClientPointers = FALSE; mdt = MiniDumpNormal; MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, mdt, (ExceptionInfo != 0) ? &mdei : 0, 0, 0 ); CloseHandle( hDumpFile ); } char szCmd[256]; sprintf_s(szCmd, 256, "TASKKILL /f /im sphost.exe"); system(szCmd); sprintf_s(szCmd, 256, "TASKKILL /f /im sphost_re.exe"); system(szCmd); DbgWithLink(LOG_LEVEL_WARN, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__).setLogCode(ERR_SPSHELL_EXCETION).setResultCode(RTAERR_SPSHELL_EXCEPTION)("spshell exit exception, saveDmp, %s, detail:%d:%s" , tmp, dmpFileStr.length() ,dmpFileStr.c_str()); ExitProcess(Error_Exception); // exit process to suppress reporting exception return EXCEPTION_EXECUTE_HANDLER; } __declspec(dllimport) bool DisableCharmbar(); __declspec(dllimport) bool EnableCharmbar(); static void AutoHideTaskBar(bool bHide) { APPBARDATA apBar; memset(&apBar, 0, sizeof(apBar)); apBar.cbSize = sizeof(apBar); apBar.lParam = bHide ? ABS_AUTOHIDE : ABS_ALWAYSONTOP; apBar.hWnd = FindWindow("Shell_TrayWnd", NULL); if (apBar.hWnd != NULL) { SHAppBarMessage(ABM_SETSTATE, &apBar); } } static bool AddFirewallRules() { char szBinDir[MAX_PATH] = {}; GetModuleFileNameA(NULL, szBinDir, MAX_PATH); *strrchr(szBinDir, SPLIT_SLASH) = 0; int nRet = (int)ShellExecute(NULL, "open", "cmd.exe", "/s /c \"netsh advfirewall firewall delete rule name=\"\"SpShell\"\"", NULL, SW_HIDE); nRet = (int)ShellExecute(NULL, "open", "cmd.exe", "/s /c \"netsh advfirewall firewall delete rule name=\"\"SpShell\"\"", NULL, SW_HIDE); nRet = (int)ShellExecute(NULL, "open", "cmd.exe", "/s /c \"netsh advfirewall firewall delete rule name=\"\"SpHost\"\"", NULL, SW_HIDE); nRet = (int)ShellExecute(NULL, "open", "cmd.exe", "/s /c \"netsh advfirewall firewall delete rule name=\"\"SpGuardian\"\"", NULL, SW_HIDE); nRet = (int)ShellExecute(NULL, "open", "cmd.exe", "/s /c \"netsh advfirewall firewall delete rule name=\"\"cefclient\"\"", NULL, SW_HIDE); char szParam[1024] = {}; sprintf_s(szParam, 1024, "/s /c \"netsh advfirewall firewall add rule name=\"\"SpShell\"\" dir=out program=\"\"%s\\spshell.exe\"\" action=allow\"", szBinDir); nRet = (int)ShellExecute(NULL, "open", "cmd.exe", szParam, NULL, SW_HIDE); sprintf_s(szParam, 1024, "/s /c \"netsh advfirewall firewall add rule name=\"\"SpHost\"\" dir=out program=\"\"%s\\sphost.exe\"\" action=allow\"", szBinDir); nRet = (int)ShellExecute(NULL, "open", "cmd.exe", szParam, NULL, SW_HIDE); sprintf_s(szParam, 1024, "/s /c \"netsh advfirewall firewall add rule name=\"\"SpHost_re\"\" dir=out program=\"\"%s\\sphost_re.exe\"\" action=allow\"", szBinDir); nRet = (int)ShellExecute(NULL, "open", "cmd.exe", szParam, NULL, SW_HIDE); sprintf_s(szParam, 1024, "/s /c \"netsh advfirewall firewall add rule name=\"\"SpGuardian\"\" dir=out program=\"\"%s\\guardian.exe\"\" action=allow\"", szBinDir); nRet = (int)ShellExecute(NULL, "open", "cmd.exe", szParam, NULL, SW_HIDE); sprintf_s(szParam, 1024, "/s /c \"netsh advfirewall firewall add rule name=\"\"cefclient\"\" dir=out program=\"\"%s\\Chromium\\cefclient.exe\"\" action=allow\"", szBinDir); nRet = (int)ShellExecute(NULL, "open", "cmd.exe", szParam, NULL, SW_HIDE); return nRet > 32; } int AddFireAddFirewallRulesThread(void* param) { if (!AddFirewallRules()) { DbgWithLink(LOG_LEVEL_FATAL, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)("设置Windows防火墙策略失败!!!"); Sleep(10000); return -1; } else DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)("设置Windows防火墙策略成功!!!"); return 0; } static void AddFirewallRulesEx() { CloseHandle(CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)& AddFireAddFirewallRulesThread, NULL, 0, NULL)); } #endif //RVC_OS_WIN /*! * at Linux, judge whether current process runs as root privilege. * @return : True only if run as admin or root */ static bool IsProcessRunAsAdmin() { BOOL bAdmin = FALSE; #ifdef RVC_OS_WIN SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; PSID AdministratorsGroup = NULL; DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)("start AllocateAndInitializeSid"); if (AllocateAndInitializeSid( &NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup)) { DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)("start CheckTokenMembership"); CheckTokenMembership(NULL, AdministratorsGroup, &bAdmin); DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)("start FreeSid"); FreeSid(AdministratorsGroup); } #else if (geteuid() == 0) { bAdmin = TRUE; } else { DbgWithLink(LOG_LEVEL_WARN, LOG_TYPE_SYSTEM)("current process is not run as root privilege, euid:%u, uid:%d", geteuid(), getuid()); } #endif //RVC_OS_WIN return bAdmin == TRUE; } const char *GetMachineType() { auto env = sp_get_env(); if (env == NULL) { DbgWithLink(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)("sp_get_env return null"); return NULL; } return env->cfg->root_ini->machine_type; } #ifdef RVC_OS_WIN const char *GetCenterSettingNameBySite(const char *pszSite) { ///*TODO(80374374@3/23/2023): CenterSettings */ #if defined(_MSC_VER) return "CenterSetting.ini"; #else if (pszSite == NULL || strlen(pszSite) == 0) return "CenterSetting.ini"; if ((stricmp(pszSite, "CMB.LIB") == 0) || (stricmp(pszSite, "CMB.SSB") == 0)) { return "CenterSetting.LAN.ini"; } else if ((stricmp(pszSite, "CMB.LSS") == 0) || (stricmp(pszSite, "CMB.FLB") == 0) || (stricmp(pszSite, "CMB.OSB") == 0) || (stricmp(pszSite, "CMB.SMM") == 0)) { return "CenterSetting.DMZ.ini"; } else { return "CenterSetting.DMZ.ini"; } #endif //_MSC_VER } #endif //RVC_OS_WIN static bool SpTerminateProcess(HANDLE hProc, DWORD dwProcID) { if (!TerminateProcess(hProc, -1)) { #ifdef RVC_OS_WIN char szCmd[256]; int nRet = 0; DbgWithLink(LOG_LEVEL_DEBUG, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)("terminate process %d fail: 0x%X, retry with taskkill", dwProcID, GetLastError()); sprintf_s(szCmd, 256, "TASKKILL /PID %d /F", dwProcID); WinExec(szCmd, SW_HIDE); return nRet != -1; #else return false; #endif //RVC_OS_WIN } return true; } void DisplayUsage() { char szHelp[4097] = { '\0' }; sprintf_s(szHelp, 4096, "\n" "Usage spshell [--entity {EntityName}][--guardian][--test][--benchmark][--kill][--debug][--ipc ][--restart][--noroot]\n" "--entity {EntityName} --启动特定的实体,选项后面输入实体名称\n" "--guardian --以后台进程的方式运行,暂未实现\n" "--test --以测试模式运行\n" "--autotest --自动化验证\n" "--debug --以调试模式运行,输出更为详细的日志\n" "--benchmark --以压力测试的模式启动\n" "--kill --直接杀死终端相关进程,比如 spshell, sphost 等\n" "--shutdown --彻底退出终端相关进程,不单独使用\n" "--systemoff --执行系统关机指令,关机前会彻底退出应用,不单独使用\n" "--reboot --执行系统重启指令,重启关机前会彻底退出应用,不单独使用\n" "--ipc --指定实体间的通讯方式,使用管道或TCP,默认使用管道\n" "--noroot -- 允许以非管理员或Root权限方式启动\n" "--restart --重启当前应用\n" "--version --显示当前的版本\n" #ifdef WITH_DEBUG "--telnet {listern port} --激活远程调用功能\n" #endif ); #ifdef RVC_OS_WIN MessageBoxA(NULL, szHelp, "Spshell Usage Tip", MB_OK); #else printf("%s\n", szHelp); #endif } static void DestroyArgs(sp_cfg_start_args_t* args) { if (args != NULL) { FREE(args->program); FREE(args->arguments); } FREE(args); } sp_cfg_start_args_t* DealWithArgs(int argc, char** argv) { int allocedEntitiyLen = 0; sp_cfg_start_args_t* args = MALLOC_T(sp_cfg_start_args_t); TOOLKIT_ASSERT(args); memset(args, 0, sizeof(sp_cfg_start_args_t)); args->program = args->arguments = NULL; if (argc > 0) { args->program = CALLOC_T(strlen(argv[0]) + 1, char); if (!args->program) { exit(-1); } memset(args->program, '\0', sizeof(char) * (strlen(argv[0]) + 1)); strcpy(args->program, argv[0]); printf("program: %s\n", args->program); std::string params; for (int i = 1, k=0; i < argc; ++i) { if(strcmp(argv[i], "--restart") == 0 || strcmp(argv[i], "-R") == 0 || strcmp(argv[i], "-Rwait")/*QT*/ == 0 || strcmp(argv[i], "wait") == 0) continue; if (k != 0) params += " "; params += argv[i]; k++; } if (!params.empty()) { args->arguments = CALLOC_T(params.length() + 1, char); if (!args->arguments) { exit(-1); } memset(args->arguments, '\0', sizeof(char) * (params.length())); strcpy(args->arguments, params.c_str()); } printf("param: %s\n", args->arguments); } args->gui = 1; args->root = 1; static struct option spshell_options[] = { {"entity", required_argument, 0, 'E' }, {"guardian", no_argument, 0, 'G'}, {"test", no_argument, 0, 'T'}, {"autotest", no_argument, 0, 'C'}, {"debug", no_argument, 0, 'D'}, {"ipc", required_argument, 0, 'I'}, {"shutdown", no_argument, 0, 'S'}, {"kill", no_argument, 0, 'K'}, {"benchmark", no_argument, 0, 'B'}, {"gui", required_argument, 0, 'U'}, #ifdef WITH_DEBUG {"telnet", required_argument, 0, 'N' }, #endif {"help", no_argument, 0, 'H'}, {"version", no_argument, 0, 'V'}, {"noroot", no_argument, 0, 'O'}, {"restart", optional_argument, 0, 'R'}, {"systemoff", optional_argument, 0, 'Y'}, {"reboot", optional_argument, 0, 'J'}, {"env", required_argument, 0, 'A' }, {0, 0, 0, 0} }; int spshell_index = 0; int c; while ((c = getopt_long(argc, argv, "E:GTCDI:SKBU:HVOR::Y::J::A:?", spshell_options, &spshell_index)) != EOF) { switch (c) { case 'C': #ifndef DEVOPS_ON_PRD //非生产情况下,才支持测试模式 args->auto_test = 1; #endif // DEVOPS_ON_PRD break; case 'E': { if (optarg != NULL) { const int len = strlen(optarg); if (args->start_entities == NULL || len + strlen(args->start_entities) + 2 /*;\0*/ >= allocedEntitiyLen) { if (args->start_entities == NULL) { TOOLKIT_ASSERT(allocedEntitiyLen == 0); allocedEntitiyLen = 128; args->start_entities = CALLOC_T(allocedEntitiyLen, char); if (NULL == args->start_entities) { DestroyArgs(args); exit(-1); } } else { TOOLKIT_ASSERT(allocedEntitiyLen != 0); const int newLen = allocedEntitiyLen + len + 32; char* newAlloc = CALLOC_T(newLen, char); if (NULL == newAlloc) { DestroyArgs(args); exit(-1); } memset(newAlloc, '\0', sizeof(char) * newLen); strcpy_s(newAlloc, newLen, args->start_entities); FREE(args->start_entities); args->start_entities = newAlloc; allocedEntitiyLen = newLen; } } strcat(args->start_entities, optarg); strcat(args->start_entities, ";"); } } break; case 'G': args->guardian_mode = 1; break; case 'D': args->debug_mode = 1; break; case 'T': args->test_mode = (args->test_mode | 1); break; case 'B': args->test_mode = (args->test_mode | 2); break; case 'I': if (optarg) { if (strnicmp(optarg, "tcp", strlen("tcp")) == 0) args->ipc_type = 1; } break; #ifdef WITH_DEBUG case 'N': if (optarg) { int nPort = 0; if (0 < sscanf(optarg, "%d", &nPort)) args->telnet_port = nPort; } break; #endif case 'K': { osutil_terminate_related_process(relate_processes_ex, array_size(relate_processes_ex), 1); DestroyArgs(args); exit(0); } break; case 'U': if (optarg) { if (strnicmp(optarg, "ON", strlen("ON")) == 0) args->gui = 1; else if(strnicmp(optarg, "OFF", strlen("OFF")) == 0) args->gui = 0; } break; case 'A': if (optarg) { if (strnicmp(optarg, "ST", strlen("ST")) == 0) args->test_mode = (args->test_mode | 4); else if (strnicmp(optarg, "UAT", strlen("UAT")) == 0) args->test_mode = (args->test_mode | 8); } break; case 'V': { DestroyArgs(args); exit(0); break; } break; case 'O': args->root = 0; break; case 'R': g_restartMode = RESTART_MODE_NORMAL; if (optarg && strcmp(optarg, "wait") == 0) { g_restartMode = RESTART_MODE_PRE; } break; case 'S': g_restartMode = RESTART_MODE_SHUTDOWN; break; case 'Y': g_restartMode = RESTART_MODE_POWEROFF; if (optarg && strnicmp(optarg, "now", strlen("now")) == 0) { g_immediately = true; } break; case 'J': g_restartMode = RESTART_MODE_REBOOT; if (optarg && strnicmp(optarg, "now", strlen("now")) == 0) { g_immediately = true; } break; case 'H': case '?': default: DisplayUsage(); DestroyArgs(args); exit(0); break; } } if (optind < argc) { while (optind < argc) { //DbgWithLink(LOG_LEVEL_WARN, LOG_TYPE_SYSTEM)("\t%s", argv[optind++]); } } if (args && args->start_entities != 0) { args->start_entities[strlen(args->start_entities) - 1] = '\0'; } return args; } void normal_signal_handle(int num) { DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM)("receive signal %d", num); signal(num, SIG_DFL); raise(num); } static bool NeedToRestartFramework() { //Shutdown 表明退掉框架不重启 return !((g_restartMode == RESTART_MODE_SHUTDOWN || g_restartMode == RESTART_MODE_POWEROFF || g_restartMode == RESTART_MODE_REBOOT)); } #if defined(_MSC_VER) ///*TODO(80374374@3/23/2023): osutil_detect_unique_app 替代 */ static bool DetectDuplicateInstance(char** pNames, int nNum) { DWORD dwCurProcID = GetCurrentProcessId(); HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); std::shared_ptr delHandleFun((void*)0, [&](void*) { if (NULL != hSnapshot) CloseHandle(hSnapshot); }); if (hSnapshot) { PROCESSENTRY32 pe = {}; pe.dwSize = sizeof(pe); if (Process32First(hSnapshot, &pe)) { do { for (int i = 0; i < nNum; i++) { if (stricmp(&pe.szExeFile[0], pNames[i]) == 0 && pe.th32ProcessID != dwCurProcID) { HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID); if (hProc == NULL) { DbgWithLink(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)("find duplicated process: %s, id: %d", pNames[i], pe.th32ProcessID); return false; } if (!SpTerminateProcess(hProc, pe.th32ProcessID)) { DbgWithLink(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)("terminate duplicated process %s fail, id: %d, error: %d", pNames[i], pe.th32ProcessID, GetLastError()); return false; } DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)("terminate duplicated process: %s, id: %d", pNames[i], pe.th32ProcessID); } } } while (Process32Next(hSnapshot, &pe)); } } else DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)("DetectDuplicateInstance can not enter Process32First!"); return true; } #endif //_MSC_VER void SPBASE_API __stdcall setCurEntityIdx(int idx); std::string generateCenterSettingPath() { char pathbuf[1024] = ""; int pathlen = ::GetModuleFileNameA(NULL, pathbuf, 1024);//获得GuiConsole路径 // 替换掉单杠 int times = 0; while (pathlen > 0) { if (pathbuf[pathlen--] == SPLIT_SLASH) times++; if (2 == times) break; } pathbuf[++pathlen] = '\0'; std::string dstPath = pathbuf; dstPath.append(SPLIT_SLASH_STR "cfg" SPLIT_SLASH_STR "CenterSetting.ini"); return dstPath; } int main(int argc, char** argv) { sp_cfg_start_args_t* args = DealWithArgs(argc, argv); #if defined(RVC_OS_LINUX) if (args->debug_mode) { SP::Perf::LeakDetector leakInstance; WLog_initRVC("SpShell"); } #else //WLog_initRVC("SpShell"); _CrtSetDebugFillThreshold(0); #endif //RVC_OS_LINUX #ifdef RVC_SAVEFILE bool saveFile = true; #else char tmp[MAX_LEN] = ""; bool saveFile = false; if (Error_Succeed == sp_tryReadFromCenterSetting("Common", "SaveFile", tmp, MAX_LEN) && 0 == CSimpleStringA(tmp).Compare("1")) saveFile = true; #endif // RVC_SAVEFILE if (saveFile) sp_dbg_init("SpShell", 1); else sp_dbg_init("SpShell", 0); EntityResource::setSaveFile(saveFile); #if defined(RVC_OS_LINUX) sp_dbg_set_level(XLOG_LEVEL_DEBUG); #endif //RVC_OS_LINUX if (argc > 1) { std::string args_str; char cmdline[1024] = { '\0' }; for (int i = 0; i < argc; ++i) { sprintf_s(cmdline, 1024, "%s", argv[i]); if (i != 0) args_str += " "; args_str += cmdline; } DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)("prefix: %s", args_str.c_str()); } #if defined(_MSC_VER) auto centersettingPath = generateCenterSettingPath(); load_debugLevelInCentersetting(centersettingPath.c_str()); load_specialRunInfoInCentersetting(centersettingPath.c_str()); auto setAffinity = [](long ulCpuMask) { HANDLE hCurrentProc; DWORD_PTR dwpProcAffinityMask = ulCpuMask; // Obtain a usable handle of the current process hCurrentProc = GetCurrentProcess(); // Get the old affinity mask SetProcessAffinityMask(hCurrentProc, dwpProcAffinityMask); SetProcessAffinityUpdateMode(hCurrentProc, PROCESS_AFFINITY_ENABLE_AUTO_UPDATE); CloseHandle(hCurrentProc); }; setAffinity(1); char* arrProcName[] = { "SpShell.exe", "SpHost.exe", "SpHost_re.exe" }; //实际可以加快这个过程,如果进程过多会比较慢 //后续可考虑用mutex进行控制 if (!DetectDuplicateInstance(&arrProcName[0], sizeof(arrProcName) / sizeof(arrProcName[0]))) { DbgWithLink(LOG_LEVEL_FATAL, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__).setResultMsg(RTAERR_SPSHELL_REPEATPROCESS)("检测到重复spshell/sphost进程,系统启动失败!!!"); Sleep(10000); return -200; } #else if (!!g_restartMode) { /** 因为没有明显的提示,等的时间(次数)不宜过长 [Gifur@202475]*/ /* 阶段一:框架收到退出事件后会调用实体的stop接口,具体实现在app.cpp::stop_all_stated_entity 阶段二:框架会温和杀死实体相关进程 阶段三:框架会强制杀死实体相关进程 */ const DWORD eachPreTimeout = 500;//等待进程自行退出的每次等待间隔时间 const int maxPreTimies = 6;//等待进程自行退出的最大等待次数 const DWORD eachTimeout = 2000;//杀死进程后的每次等待间隔时间 const int maxTimes = 15;//杀死进程后的最大等待次数 const int gentleKillThresholdTimes = 10; //允许温和杀死实体进程的次数 const DWORD skipInterval = 5; int currTimes = 0, preTimes = 0; bool needWait = (g_restartMode > RESTART_MODE_NORMAL && !g_immediately); //Rwait 框架触发的重启 const bool needlessRestart = !NeedToRestartFramework(); int aliveCount = 0, lastAliveCount(1000); char** relates = relate_processes; int reletes_cnt = relate_processes_count; if (needWait) { DbgWithLink(LOG_LEVEL_DEBUG, LOG_TYPE_SYSTEM)("等待应用进程退出..."); } if (needlessRestart) { DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM)("to shutdown guardian..."); char* tmp_processes[] = { guardian_execute_name }; osutil_terminate_related_process(tmp_processes, array_size(tmp_processes), 1); relates = relate_processes_ex; reletes_cnt = relate_processes_ex_count; } bool switch_flag = false; while (switch_flag || !osutil_detect_unique_app(relates, reletes_cnt, &aliveCount, NULL)) { if (!needWait && currTimes++ >= maxTimes) { DbgWithLink(LOG_LEVEL_FATAL, LOG_TYPE_SYSTEM)("detect ex-spshell or sphost process, abort cur boot !!!"); DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM)("等待应用进程退出超时,请尝试重启机器!"); sp_dbg_term(); DestroyArgs(args); return -200; } else { if (needWait) { Sleep(eachPreTimeout); if (preTimes++ >= maxPreTimies && (aliveCount >= lastAliveCount)) { needWait = false; DbgWithLink(LOG_LEVEL_DEBUG, LOG_TYPE_SYSTEM)("set need wait flag false"); } DbgWithLink(LOG_LEVEL_DEBUG, LOG_TYPE_SYSTEM)("wait... last active process count:%d, curr process count: %d, ppid: %d", lastAliveCount, aliveCount, GetParentProcessID()); lastAliveCount = aliveCount; } else { const int force_flag = gentleKillThresholdTimes < currTimes ? 1 : 0; DbgWithLink(LOG_LEVEL_WARN, LOG_TYPE_SYSTEM)("detect ex-spshell or sphost process %d, %d, %d terminate all !!!", preTimes, currTimes, force_flag); osutil_terminate_related_process(relates, reletes_cnt, force_flag); Sleep(eachTimeout); } const DWORD consumedTime = (currTimes * eachTimeout) / 1000 + (eachPreTimeout * preTimes) / 1000; if (consumedTime > 0) { DbgWithLink(LOG_LEVEL_DEBUG, LOG_TYPE_SYSTEM)("等待应用进程退出,已等待 %d 秒...", consumedTime); } } //aliveCount = 0; //important!!! switch_flag = (((preTimes + currTimes) % skipInterval) != 0); if (!switch_flag) { aliveCount = 0; DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM)("will enter osutil_detect_unique_app"); } } DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM)("finish detecting unique app."); if (needlessRestart) { const DWORD sleepMillsecs = 2000; DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM)("restart mode: %d", g_restartMode); if (g_restartMode == RESTART_MODE_SHUTDOWN) { DbgWithLink(LOG_LEVEL_DEBUG, LOG_TYPE_SYSTEM)("应用进程已完全清理,退出当前窗口..."); } else if (g_restartMode == RESTART_MODE_POWEROFF) { DbgWithLink(LOG_LEVEL_DEBUG, LOG_TYPE_SYSTEM)("应用进程已完全清理,准备关机..."); osutil_shutdown_system(); DbgWithLink(LOG_LEVEL_DEBUG, LOG_TYPE_SYSTEM)("应用进程已完全清理,正在关机..."); } else if (g_restartMode == RESTART_MODE_REBOOT) { DbgWithLink(LOG_LEVEL_DEBUG, LOG_TYPE_SYSTEM)("应用进程已完全清理,准备关机重启..."); osutil_restart_system(); DbgWithLink(LOG_LEVEL_DEBUG, LOG_TYPE_SYSTEM)("应用进程已完全清理,正在关机并重启..."); } Sleep(sleepMillsecs); sp_dbg_term(); DestroyArgs(args); return 0; } } else if (!osutil_detect_unique_app(relate_processes, array_size(relate_processes), NULL, NULL)) { do { bool clearTotal = false; int innerClearEnhance = 0; do { osutil_terminate_related_process(relate_processes, array_size(relate_processes), innerClearEnhance > 8 ? 1 : 0); Sleep(3000); int count = MAX_ALIVE_PROCESS_COUNT; alive_process_info processes[MAX_ALIVE_PROCESS_COUNT]; memset(processes, 0, sizeof(processes)); if (osutil_detect_unique_app(relate_processes, array_size(relate_processes), &count, processes)) { clearTotal = true; break; } } while (innerClearEnhance++ < 10); if (clearTotal) { break; } DbgWithLink(LOG_LEVEL_WARN, LOG_TYPE_SYSTEM)("clear ex-spshell/sphost process failed!"); sp_dbg_term(); DestroyArgs(args); return -200; } while (false); } #endif //_MSC_VER sp_runtask_loadLogLevel(); sp_tryquickStartCef(); create_log_producer_default("SpShell", 0); DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)("===================SpShell start====================="); DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)("process id: %d, saveFile:%d", GetCurrentProcessId(), saveFile); if (argc > 1) { std::string args_str; char cmdline[1024] = { '\0' }; for (int i = 0; i < argc; ++i) { sprintf_s(cmdline, 1024, "%s", argv[i]); if (i != 0) args_str += " "; args_str += cmdline; } DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)(args_str.c_str()); SP_TRACE(args_str.c_str()); } if (!IsProcessRunAsAdmin()) { if (args->root) { DbgWithLink(LOG_LEVEL_FATAL, LOG_TYPE_SYSTEM)("current process need run with administrator or root privilege !!!"); DbgWithLink(LOG_LEVEL_FATAL, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__).setResultMsg(RTAERR_SPSHELL_NOPRIVILEGE)("需要以管理员权限运行!!!"); Sleep(10000); sp_dbg_term(); DestroyArgs(args); return -201; } else { DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)("current process requires normal privilege."); } } else { DbgWithLink(LOG_LEVEL_DEBUG, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)("current process has been run with administrator or root privilege."); args->root = (char)1; #if defined(_MSC_VER) SYSTEM_INFO SystemInfo; GetSystemInfo(&SystemInfo); DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)("当前以管理员权限运行, curProcessId:%d, curVersion:%s ,dwNumberOfProcessors=%u, dwActiveProcessorMask=%u, wProcessorLevel=%u, wProcessorArchitecture=%u, dwPageSize=%u", GetCurrentProcessId(), STRFILEVER, SystemInfo.dwNumberOfProcessors, SystemInfo.dwActiveProcessorMask, SystemInfo.wProcessorLevel, SystemInfo.wProcessorArchitecture, SystemInfo.dwPageSize); #endif //_MSC_VER } #ifdef RVC_OS_WIN DbgWithLink(LOG_LEVEL_DEBUG, LOG_TYPE_SYSTEM)("Config firewall policies......"); // 设置防火墙注册表配置 AddFirewallRulesEx(); setlocale(LC_ALL, "chs"); #else if (NULL == setlocale(LC_ALL, "zh_CN.UTF-8")) { //zh_CN.UTF-8 zh_CN.GBK DbgWithLink(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM)("setlocale failed: %s", strerror(errno)); } #endif //RVC_OS_WIN ResetEnvPath(); #ifdef RVC_OS_WIN DbgWithLink(LOG_LEVEL_DEBUG, LOG_TYPE_SYSTEM)("Config working set......"); SetWorkingSet();//获取设置内存使用率,4G #endif //RVC_OS_WIN DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)("basic config success"); #ifdef RVC_OS_WIN // 检测是否Win8及64位 OSVERSIONINFO ver = {}; ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&ver); bool bWin8 = (ver.dwMajorVersion >= 7 || (ver.dwMajorVersion == 6 && ver.dwMinorVersion >= 2)); //version over 7 or over 6.2 SYSTEM_INFO sysInfo = {}; GetNativeSystemInfo(&sysInfo); bool bX64 = (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64); DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)("OS version: %s %d.%d.%d , SetErrorMode %s", bX64 ? "windows 64" : "windows 32", ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber, 0 == SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX) ? "success" : "failed"); SetUnhandledExceptionFilter(&SuppressError); #endif //RVC_OS_WIN auto msg_lamda_func = [&](const char* msg, bool ctritial)->void { }; ////////////////////////////////////////////////////////////////////////// #if defined(_MSC_VER) auto rc = app_init(args); int result = rc.first; #else int result = app_init(args, msg_lamda_func); #endif //_MSC_VER ////////////////////////////////////////////////////////////////////////// if (result == 0) { sp_trace_term(); DbgWithLink(LOG_LEVEL_DEBUG, LOG_TYPE_SYSTEM)("run circle loop in main thread!!"); result = app_run(); app_term(); } else { uint32_t waitIntervals = 10000; if (sp_trace_exist()) { char* last_err = NULL; sp_trace_retrieve(&last_err, NULL); if (last_err) { DbgWithLink(LOG_LEVEL_DEBUG, LOG_TYPE_SYSTEM)("to show last error dialog..."); DbgWithLink(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM)("应用启动失败:%s", last_err); waitIntervals = 1; FREE(last_err); DbgWithLink(LOG_LEVEL_DEBUG, LOG_TYPE_SYSTEM)("to show last error dialog done!"); } sp_trace_term(); } #if defined(_MSC_VER) DbgWithLink(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)("======================================================"); DbgWithLink(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__).setResultMsg(RTAERR_SPSHELL_APPINITERR)(rc.second.GetData()); DbgWithLink(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)("!!!!!! 启动失败,请检查dbg\\spshell日志排除故障 !!!!!!"); DbgWithLink(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)("======================================================"); #else DbgWithLink(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)("======================================================"); DbgWithLink(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__).setResultMsg(RTAERR_SPSHELL_APPINITERR)("!!!!!! Startup failed, get more detail information from dbg/spshell !!!!!!"); DbgWithLink(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM).setAPI(__FUNCTION__)("======================================================"); #endif //_MSC_VER app_upload_last_log(); Sleep(waitIntervals); } #ifdef RVC_OS_WIN if (bWin8 && !bX64) { EnableCharmbar(); } #endif //RVC_OS_WIN sp_dbg_term(); DestroyArgs(args); return result; } #ifdef RVC_OS_WIN int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { return main(__argc, __argv); } #endif //RVC_OS_WIN