// guardian.cpp : Defines the entry point for the console application. // #include "stdafx.h" #ifdef linux #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "toolkit.h" #include "osutil.h" #include "memutil.h" #include #include #include #include int epfd; struct epoll_event ev; #define MAXLINE 128 #define OPEN_MAX 100 #define LISTENQ 20 #define INFTIM 1000 #else #undef UNICODE #pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"" ) #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include #include #include #include #include #endif //linux #include #include #include #include #include "GuardianBase.h" #include "guardian.h" #include "log4rvcother.h" #ifndef MAX_PATH #define MAX_PATH 260 #endif #define GUARDIAN_VERSION_STR "0.0.2" #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 char* relate_processes[] = { spshell_execute_name }; char* relate_processes_ex[] = { spshell_execute_name, sphost_execute_name, cefclient_execute_name }; using namespace std; #ifdef linux int g_sListen; pthread_mutex_t g_cs, g_cs_event, g_cs_log; #define EnterCriticalSectionRVC(xType) pthread_mutex_lock(&xType) #define LeaveCriticalSectionRVC(xType) pthread_mutex_unlock(&xType) static int GetProcID() { return getpid(); } #else SOCKET g_sListen = INVALID_SOCKET; DWORD WINAPI DoNetControl(void* pData); CRITICAL_SECTION g_cs, g_cs_event, g_cs_log; #define EnterCriticalSectionRVC(xType) EnterCriticalSection(&xType) #define LeaveCriticalSectionRVC(xType) LeaveCriticalSection(&xType) static int GetProcID() { return GetCurrentProcessId(); } #endif //linux const int DEFAULT_BUFLEN = 512; const int SHAKEHAND_BUFZIE = 32; #define DEFAULT_PORT "30005" const int default_port = 30005; int g_needToRollBack = 0; bool g_needUpgradeGuardianOnly = false; bool g_bFrameQuit = false,g_bFrameOnline = false,g_bAuthSuc = false,g_bInUpgrade = false; ULONGLONG g_dwTimeBegin = 0; ULONGLONG g_dwUpgradeRestartTimeBegin = 0; int cnt = 0; char chDisk[2]; char currDirBuf[256]; const int MAX_WAIT_TIME_TO_RESTART = 600000; //oilyang@20211208 change value from 110000 to 180000 const int MAX_WAIT_TIME_TO_RESTART_UPGRADE = 180000; static int g_SpShellPID = 0; #ifdef linux extern "C" { void* DoWorkLinux(void* arg); void* DoNetControlLinux(void* arg); } #define FUNCTION_STDCALL typedef unsigned short WORD; #else typedef struct { OVERLAPPED Overlapped; WSABUF DataBuf; CHAR Buffer[DATA_BUFSIZE]; } PER_IO_OPERATION_DATA, * LPPER_IO_OPERATION_DATA; typedef struct { SOCKET Socket; } PER_HANDLE_DATA, * LPPER_HANDLE_DATA; DWORD WINAPI DoWork(void* pData); #define FUNCTION_STDCALL __stdcall #endif int BeginThreadRVC(void* (*pFuncLinux)(void*), LPTHREAD_START_ROUTINE pFuncWin) { #if defined(_MSC_VER) CreateThread(NULL, 0, pFuncWin, NULL, 0, NULL); return 0; #else pthread_t tidp; if ((pthread_create(&tidp, NULL, pFuncLinux, NULL) == -1)) { return 1; } cout << "after pthread_create of linux thread" << endl; return 0; #endif //_MSC_VER } void EndThreadRVC() { #ifdef linux pthread_exit(0); #endif //linux } void StartDoNetControlRVC() { #ifdef linux BeginThreadRVC(DoNetControlLinux, NULL); #else BeginThreadRVC(NULL, DoNetControl); #endif //linux } void StartDoWorkRVC() { #ifdef linux BeginThreadRVC(DoWorkLinux, NULL); #else BeginThreadRVC(NULL, DoWork); #endif //linux } void ServerReportEvent(const char* szFunction,const char* szName="guardian") { #ifdef linux return;//oiltest #else HANDLE hEventSource; LPCTSTR lpszStrings[2]; char Buffer[1024] = {0}; hEventSource = RegisterEventSource(NULL, szName); if( NULL != hEventSource ) { strcpy_s(Buffer,1024,szFunction); lpszStrings[0] = szName; lpszStrings[1] = Buffer; ReportEvent(hEventSource, // event log handle EVENTLOG_ERROR_TYPE, // event type 0, // event category 0xe0000001, // event identifier NULL, // no security identifier 2, // size of lpszStrings array 0, // no binary data lpszStrings, // array of strings NULL); // no binary data DeregisterEventSource(hEventSource); } #endif //linux } bool VersionRollback() { const int VersionLenMax = 64; fstream verFile,verBak;//to confirm string strActiveTxtPath, strVersionDatPath; #ifdef linux char tmp[MAX_PATH]; char* pos = NULL; GetModuleFileNameA(NULL, tmp, MAX_PATH); LOG4VTM(INFO, "rollback" << tmp); if ((pos = strstr(tmp, "/Run/")) != NULL) { pos[strlen("/Run/")] = '\0'; strActiveTxtPath = string(tmp) + "version/active.txt"; strVersionDatPath = string(tmp) + "runinfo/runcfg/version.dat"; } else { strActiveTxtPath = "/opt/Run/version/active.txt"; strVersionDatPath = "/opt/Run/runinfo/runcfg/version.dat"; } #else string strDisk(chDisk); strActiveTxtPath = strDisk + ":\\Run\\version\\active.txt"; strVersionDatPath = strDisk + ":\\Run\\runinfo\\runcfg\\version.dat"; #endif //linux verFile.open(strActiveTxtPath,std::fstream::in|std::fstream::out|std::fstream::binary); verBak.open(strVersionDatPath,std::fstream::in|std::fstream::out|std::fstream::binary); if (!verFile.is_open() || !verBak.is_open()) return false; char *pVerBak,*pNULL,*pCurrVer; pVerBak = new char[VersionLenMax]; pNULL = new char[VersionLenMax]; pCurrVer = new char[VersionLenMax]; memset(pVerBak,0,VersionLenMax); memset(pNULL,0,VersionLenMax); memset(pCurrVer,0,VersionLenMax); verBak.seekg(0,verBak.end); int lenBak = verBak.tellg(); verBak.seekg(0,verBak.beg); verBak.read(pVerBak,lenBak); //need to consider atomic op? oilyang verFile.seekg(0,verFile.end); int len = verFile.tellg(); verFile.seekg(0,verFile.beg); verFile.read(pCurrVer,len); LOG4VTM(INFO, pVerBak); LOG4VTM(INFO, pCurrVer); LOG4VTM(INFO, pNULL); if (strcmp(pVerBak,pCurrVer) == 0) { ServerReportEvent(pVerBak); verFile.close(); verBak.close(); delete []pVerBak; delete []pNULL; return false; } //verBak.write(pNULL,len); //verFile.write(pNULL,len); //how foolish? verFile.close(); verFile.open(strActiveTxtPath,std::fstream::in|std::fstream::out|std::fstream::binary|std::fstream::trunc); verFile.seekg(0,verFile.beg); verFile.write(pVerBak,lenBak); LOG4VTM(INFO, "terminal app version will rollback from " << pCurrVer << " to " << pVerBak); verFile.close(); verBak.close(); delete []pVerBak; delete []pNULL; return true; } #ifndef _WIN32 static int GetSpshellProcID() { int result(0); char* spshell[] = { spshell_execute_name }; alive_process_info processes[1]; memset(processes, 0, sizeof(processes)); int count = 1; if (!osutil_detect_unique_app(spshell, array_size(spshell), &count, processes)) { result = processes[0].pid; } return result; } static void ComfirmSpShellDead() { char* spshell[] = { spshell_execute_name }; alive_process_info processes[1]; memset(processes, 0, sizeof(processes)); int count = 1; if (!osutil_detect_unique_app(spshell, array_size(spshell), &count, processes)) { if(g_SpShellPID == 0) g_SpShellPID = processes[0].pid; LOG4VTM(WARN, "has spshell process alive! and to kill it"); assert(count > 0); if (kill(processes[0].pid, SIGTERM) != 0) { LOG4VTM(WARN, "kill with term for spshell failed, process id: " << processes[0].pid << ", err: " << errno); if (errno != 3 /**not exist */) { LOG4VTM(INFO, "to kill stronglely!"); kill(processes[0].pid, SIGKILL); } } else { LOG4VTM(DEBUG, "kill with term for spshell succ. process id: " << processes[0].pid); } const int waitTimes = 100; const int eachTime = 300; int curTimes = 0; while (g_SpShellPID != 0 && curTimes < waitTimes) { if (osutil_detect_unique_app(spshell, array_size(spshell), NULL, NULL)) { g_SpShellPID = 0; break; } Sleep(eachTime); curTimes++; } if (g_SpShellPID == 0) { LOG4VTM(DEBUG, "spshell has gone!"); } } else { LOG4VTM(INFO, "There are no any spshell process existed!"); } ////////////////////////////////////////////////////////////////////////// count = MAX_ALIVE_PROCESS_COUNT; alive_process_info processes2[MAX_ALIVE_PROCESS_COUNT]; memset(processes2, 0, sizeof(processes2)); if (!osutil_detect_unique_app(relate_processes_ex, array_size(relate_processes_ex), &count, processes2)) { for (int i = 0; i < count; ++i) { LOG4VTM(INFO, processes2[i].name << " " << processes2[i].path << " " << processes2[i].pid); } osutil_terminate_related_process(relate_processes_ex, array_size(relate_processes_ex), 0); Sleep(1000); } } inline static std::vector Split(std::string str, char splitElem) { std::vector strs; std::string::size_type pos1, pos2; pos2 = str.find(splitElem); pos1 = 0; while (std::string::npos != pos2) { strs.push_back(str.substr(pos1, pos2 - pos1)); pos1 = pos2 + 1; pos2 = str.find(splitElem, pos1); } strs.push_back(str.substr(pos1)); return strs; } static bool IsSpPathType(const std::string& value) { const int leastLength = strlen("Run/version/1.0.0.0/"); std::string path(value.c_str()); if (path.empty() || path.size() < leastLength) return false; const std::size_t sionPos = path.find("version"); const std::size_t verPos = sionPos + strlen("version\\"); if (sionPos == std::string::npos) return false; std::size_t suffixPos = std::string::npos, i; std::size_t dotCnt = 0; bool lastIsNum = false; for (i = verPos; i < path.size() && (path[i] >= '0' && path[i] <= '9' || path[i] == '.'); ++i) { lastIsNum = !(path[i] == '.'); if (!lastIsNum) dotCnt++; } if (i >= path.size() || dotCnt != 3 || !lastIsNum) return false; return true; } /** 移除跟版本目录相关的环境变量信息*/ static std::string CutSpPathFromPathValue(const char* value) { std::string path(value); std::string result(""); std::vector values = Split(path, ':'); if (values.size() <= 0) { return path; } for (auto i = values.cbegin(); i != values.cend(); i++) { if (!IsSpPathType(*i)) { if (!result.empty()) result += ":"; result += (*i); } } if (!result.empty() && path[path.size() - 1] == ':') { result += ":"; } return result; } static void ResetEnviromentVars(const char* env) { DWORD size; char* buf; int len = 0; size = GetEnvironmentVariableA(env, NULL, 0); if (size == 0) { return; } len = size + MAX_PATH * 3; buf = (char*)malloc(len); memset(buf, 0, sizeof(buf)); size = GetEnvironmentVariableA(env, buf, len); std::string newValue = CutSpPathFromPathValue(buf); if (newValue.empty()) { SetEnvironmentVariableA(env, NULL); } else if (newValue.size() < size) { strcpy(buf, newValue.c_str()); SetEnvironmentVariableA(env, buf); } #if 1 memset(buf, 0, sizeof(buf)); size = GetEnvironmentVariableA(env, buf, len); LOG4VTM(INFO, buf << "env: " << env << " size: " << size); #endif FREE(buf); } static void ResetRelateEnviromentVars( const char* prefix) { LOG4VTM(INFO, prefix); ResetEnviromentVars("LD_LIBRARY_PATH"); ResetEnviromentVars("PATH"); } #endif //NOT _WIN32 int FrameworkShutdown(bool bUpgrade=false,bool bRestart = true) { LOG4VTM(INFO, "in FrameworkShutdown,bUpgrade:" << bUpgrade << ",bRestart:" << bRestart); if (!bUpgrade) g_dwTimeBegin = GetTickCount64(); #ifdef linux //todo oiltestlinux LOG4VTM(DEBUG, "to kill spshell"); ServerReportEvent("FrameworkShutdown linux"); ComfirmSpShellDead(); char tmp[MAX_PATH]; char* pos = NULL; GetModuleFileNameA(NULL, tmp, MAX_PATH); LOG4VTM(INFO, "FrameworkShutdown: " << tmp); if ((pos = strstr(tmp, "/version")) != NULL) { pos[strlen("/version")+1] = '\0'; ResetRelateEnviromentVars(tmp); char path[MAX_PATH] = { '\0' }; bool toResetCWD(false); LOG4VTM(DEBUG, "to get current directory..."); GetCurrentDirectoryA(MAX_PATH, path); LOG4VTM(DEBUG, path); if (strlen(path) <= 0 || strcmp(path, tmp) != 0) { LOG4VTM(INFO, "to set current directory..."); SetCurrentDirectoryA(tmp); toResetCWD = true; } strcat(tmp, "spexplorer.sh --restart"); LOG4VTM(DEBUG, tmp); tk_process_t* process = NULL; tk_process_option_t option; option.exit_cb = NULL; option.file = NULL; option.flags = 0; option.params = tmp; if (0 == process_spawn(&option, &process)) { FREE(process); LOG4VTM(INFO, "run spexplorer.sh scripts succ."); Sleep(1000); g_SpShellPID = GetSpshellProcID(); LOG4VTM(INFO, "get spshell proc id: " << g_SpShellPID); if (toResetCWD) { SetCurrentDirectoryA(path); } return 0; } else { FREE(process); LOG4VTM(ERROR, "run spexplorer.sh scripts failed!"); if (toResetCWD) { SetCurrentDirectoryA(path); } return -1; } } LOG4VTM(WARN, "get regular version path failed!"); return 0; #else STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); ZeroMemory( &pi, sizeof(pi) ); // Start the child process. std::string csRestart,csVerPath,csAll,csSep("\""),csBlank(" "),csScript("wscript.exe"),csReFlag("r"); csRestart = "sprestart.exe "; csVerPath = chDisk; csVerPath = csVerPath + std::string(":\\Run\\version"); csVerPath = csVerPath + std::string("\\VTM.exe"); if (!bRestart) csReFlag = "n"; csAll = csSep + csRestart + csSep + csBlank + csSep + csVerPath + csSep + csBlank + csSep + csReFlag + csSep; char* szCmdline = new char[csAll.length() + 1]; memset(szCmdline, '\0', csAll.length() + 1); strcpy(szCmdline, csAll.c_str()); if( !CreateProcess( NULL,szCmdline,NULL,NULL,FALSE,CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi)) { delete[] szCmdline; return -1; } DWORD dwErr = GetLastError(); // Wait until child process exits. WaitForSingleObject( pi.hProcess, INFINITE ); // Close process and thread handles. CloseHandle( pi.hProcess ); CloseHandle( pi.hThread ); delete[] szCmdline; return 0; #endif //linux } bool FrameworkRollBack() { bool bVerRollback = VersionRollback(); LOG4VTM(INFO, "version rollback "); if (true) FrameworkShutdown(); return true; } bool ReadRuninfoContent(string &strData) { fstream runinfo; string strGdRuninfoPath; #ifdef linux //todo oiltestlinux char tmp[MAX_PATH]; char* pos = NULL; GetModuleFileNameA(NULL, tmp, MAX_PATH); if ((pos = strstr(tmp, "/Run/")) != NULL) { pos[strlen("/Run/")] = '\0'; strGdRuninfoPath = string(tmp) + "runinfo/runcfg/gdruninfo"; } else { strGdRuninfoPath = "/opt/Run/runinfo/runcfg/gdruninfo"; } #else string strDisk(chDisk); strGdRuninfoPath = strDisk + ":\\Run\\runinfo\\runcfg\\gdruninfo"; #endif //linux LOG4VTM(INFO, strGdRuninfoPath.c_str()); runinfo.open(strGdRuninfoPath,std::fstream::in|std::fstream::out|std::fstream::binary); if (!runinfo.is_open()) { LOG4VTM(INFO, "open gdruninfo(read) failed."); return false; } runinfo.seekg(0,ios::end); int size = runinfo.tellg(); if (size <= 0) return false; char *pData = new char[size+1]; memset(pData,0,size+1); runinfo.seekg(0,ios::beg); runinfo.read(pData,size); strData = string(pData); LOG4VTM(INFO, "read runinfo " << strData); return true; } bool WriteRunInfoContent(const char* pData) { ofstream runinfo; string strGdRuninfoPath; #ifdef linux //todo oiltestlinux char tmp[MAX_PATH]; char* pos = NULL; GetModuleFileNameA(NULL, tmp, MAX_PATH); LOG4VTM(INFO, tmp); if ((pos = strstr(tmp, "/Run/")) != NULL) { pos[strlen("/Run/")] = '\0'; strGdRuninfoPath = string(tmp) + "runinfo/runcfg/gdruninfo"; } else { strGdRuninfoPath = "/opt/Run/runinfo/runcfg/gdruninfo"; } LOG4VTM(INFO, strGdRuninfoPath.c_str()); #else string strDisk(chDisk); strGdRuninfoPath = strDisk + ":\\Run\\runinfo\\runcfg\\gdruninfo"; #endif //linux runinfo.open(strGdRuninfoPath,std::ofstream::in|std::ofstream::binary|std::ofstream::trunc); if (!runinfo.is_open()) { LOG4VTM(INFO, "open gdruninfo(write) failed."); ServerReportEvent("open gdruninfo(write) failed."); return false; } runinfo.write(pData,strlen(pData)); runinfo.close(); LOG4VTM(INFO, "write " << strGdRuninfoPath << " done"); return true; } bool IsStartTimeFileExist() { ofstream runinfo; string strStartTimePath; #ifdef linux //todo oiltestlinux char tmp[MAX_PATH]; char* pos = NULL; GetModuleFileNameA(NULL, tmp, MAX_PATH); LOG4VTM(INFO, "IsStartTimeFileExist: " << tmp); if ((pos = strstr(tmp, "/Run/")) != NULL) { pos[strlen("/Run/")] = '\0'; strStartTimePath = string(tmp) + "runinfo/runcfg/starttime.dat"; } else { strStartTimePath = "/opt/Run/runinfo/runcfg/starttime.dat"; } #else string strDisk(chDisk); strStartTimePath = strDisk + ":\\Run\\runinfo\\runcfg\\starttime.dat"; #endif //linux runinfo.open(strStartTimePath, std::fstream::in | std::fstream::out | std::fstream::binary); if (!runinfo.is_open()) { LOG4VTM(ERROR, "open starttime.dat failed"); return false; } else { runinfo.close(); LOG4VTM(DEBUG, "open starttime.dat succ"); return true; } } #ifdef linux void CheckDoWork(int sig) { cout << "CheckDoWork" << endl; if (SIGALRM == sig) { EnterCriticalSectionRVC(g_cs); if (!g_bFrameQuit) { ULONGLONG dwTmpBegin = g_dwTimeBegin; ULONGLONG dwUpgradeTmpBegin = g_dwUpgradeRestartTimeBegin; LeaveCriticalSectionRVC(g_cs); ULONGLONG dwTimeEnd = GetTickCount64(); //oilyang@20190828 add //升级重启后,在10分钟内,只要离最后一次交互时间大于2分钟,重启框架 if ((g_bInUpgrade && ((dwTimeEnd - dwUpgradeTmpBegin) < MAX_WAIT_TIME_TO_RESTART)) && (!g_bAuthSuc && ((dwTimeEnd - dwTmpBegin) > MAX_WAIT_TIME_TO_RESTART_UPGRADE))) { LOG4VTM(WARN, "**in upgrade restart**,to restart framework(linux)."); FrameworkShutdown(true, true); } if ((dwTimeEnd - dwTmpBegin) > MAX_WAIT_TIME_TO_RESTART) { LOG4VTM(ERROR, "framework maybe down."); string strRunInfo; if (ReadRuninfoContent(strRunInfo)) { LOG4VTM(INFO, "to check if need rollback."); LOG4VTM(INFO, (char*)strRunInfo.c_str()); if (strRunInfo.compare("111") == 0 && IsStartTimeFileExist()) { EnterCriticalSectionRVC(g_cs); g_needToRollBack = 1; LeaveCriticalSectionRVC(g_cs); } } else LOG4VTM(WARN, "read run info failed."); if (g_needToRollBack) { LOG4VTM(INFO, "after upgrade,time elapse,but can't wait the shake hands,so rollback."); FrameworkRollBack(); } else { //framework is down,to restart it! oilyang 20150413 FrameworkShutdown(); } } } else LeaveCriticalSectionRVC(g_cs); alarm(2 * 60); //we contimue set the timer } return; } void* DoWorkLinux(void* arg) { LOG4VTM(DEBUG, "DoWorkLinux"); signal(SIGALRM, CheckDoWork); //relate the signal and function alarm(2 * 60); //trigger the timer EndThreadRVC(); } void* DoNetControlLinux(void* arg) { LOG4VTM(DEBUG, "to end DoNetControlLinux"); EndThreadRVC(); LOG4VTM(DEBUG, "after end DoNetControlLinux"); } void DataProcessLinux(int socket,const char*data) { GuardianInfo* pInfo = (GuardianInfo*)data; LOG4VTM(DEBUG, "eType:" << pInfo->eType << ",p1:" << pInfo->dwParam1 << ",p2:" << pInfo->dwParam2 << ",dwSize:" << pInfo->dwSize); switch (pInfo->eType) { case GdOpShakeHand: { if (!g_bFrameOnline) { g_bFrameOnline = true; g_SpShellPID = GetSpshellProcID(); LOG4VTM(INFO, "get spshell proc id" << g_SpShellPID); StartDoNetControlRVC(); } int rc, err; EnterCriticalSectionRVC(g_cs); g_bFrameQuit = false; g_dwTimeBegin = GetTickCount64(); LeaveCriticalSectionRVC(g_cs); char* pBuf = new char[8]; memset(pBuf, 0, 8); //get guardian state //ex:u(update),g(guardian),r(reboot),b(rollback) if (cnt % 2 == 0) memcpy(pBuf, "g", 1); else memcpy(pBuf, "u", 1); cnt++; rc = send(socket, pBuf, 8, 0); delete[]pBuf; close(socket); } break; case GdOpUpdateTask: { if (!g_bFrameOnline) { g_bFrameOnline = true; g_SpShellPID = GetSpshellProcID(); LOG4VTM(INFO, "get spshell proc id" << g_SpShellPID); StartDoNetControlRVC(); } } break; case GdOpQueryInstall: { if (!g_bFrameOnline) { g_bFrameOnline = true; g_SpShellPID = GetSpshellProcID(); LOG4VTM(INFO, "get spshell proc id" << g_SpShellPID); StartDoNetControlRVC(); } int rc, err; char* pBuf = new char[8]; memset(pBuf, 0, 8); if (cnt % 2 == 0) memcpy(pBuf, "y", 1); else memcpy(pBuf, "n", 1); cnt++; rc = send(socket, pBuf, 8, 0); delete[]pBuf; close(socket); } break; case GdOpUpgradeRestart: { if (pInfo->dwParam1 == 6)//HealthManager call guardian to quit { LOG4VTM(INFO, "HealthManager call guardian to quit"); close(socket); close(g_sListen); exit(0);//baoli? break; } if (pInfo->dwParam1 == 4)//just know framework is starting... { LOG4VTM(INFO, "framework is starting..."); g_bFrameOnline = true; g_SpShellPID = GetSpshellProcID(); LOG4VTM(INFO, "get spshell proc id" << g_SpShellPID); g_bAuthSuc = false; StartDoNetControlRVC(); LOG4VTM(DEBUG, "to break upgrade..."); close(socket); break; } if (pInfo->dwParam1 == 5) { LOG4VTM(INFO, "framework auth ok."); g_bInUpgrade = false; g_bAuthSuc = true; } if (!g_bFrameOnline) { g_bFrameOnline = true; g_SpShellPID = GetSpshellProcID(); LOG4VTM(INFO, "get spshell proc id" << g_SpShellPID); StartDoNetControlRVC(); } if (pInfo->dwParam1 == 1 || pInfo->dwParam1 == 3) g_dwTimeBegin = GetTickCount64(); ServerReportEvent("upgrade restart"); LOG4VTM(INFO, "upgrade restart " << pInfo->dwParam1); if (pInfo->dwParam1 == 1)//framework to restart after upgrade { g_bInUpgrade = true; WriteRunInfoContent("111"); g_dwUpgradeRestartTimeBegin = GetTickCount64(); } else if (pInfo->dwParam1 == 2)//need rollback after upgrade { LOG4VTM(INFO, "upgrade restart 222"); WriteRunInfoContent("222"); //rollback EnterCriticalSectionRVC(g_cs); g_needToRollBack = 0; LeaveCriticalSectionRVC(g_cs); LOG4VTM(INFO, "healthmanager said to rollback"); FrameworkRollBack(); } else if (pInfo->dwParam1 == 3)//upgrade succeeded { LOG4VTM(INFO, "upgrade restart ok"); WriteRunInfoContent("333"); EnterCriticalSectionRVC(g_cs); g_needToRollBack = 0; LeaveCriticalSectionRVC(g_cs); } //runinfo.close(); close(socket); } break; case GdOpFrameQuit: if (!g_bFrameOnline) { g_bFrameOnline = true; g_SpShellPID = GetSpshellProcID(); LOG4VTM(INFO, "get spshell proc id" << g_SpShellPID); StartDoNetControlRVC(); } EnterCriticalSectionRVC(g_cs); g_bFrameQuit = true; LeaveCriticalSectionRVC(g_cs); close(socket); break; default: break; } LOG4VTM(DEBUG, "quit of DataProcessLinux"); } #else void DataProcess(LPPER_HANDLE_DATA pPerHandleData,LPPER_IO_OPERATION_DATA pPerIoData) { GuardianInfo* pInfo = (GuardianInfo*)pPerIoData->Buffer; switch(pInfo->eType) { case GdOpShakeHand: { if (!g_bFrameOnline) { g_bFrameOnline = true; StartDoNetControlRVC(); } int rc,err; EnterCriticalSectionRVC(g_cs); g_bFrameQuit = false; g_dwTimeBegin = GetTickCount64(); LeaveCriticalSectionRVC(g_cs); char* pBuf = new char[8]; memset(pBuf,0,8); //get guardian state //ex:u(update),g(guardian),r(reboot),b(rollback) if (cnt%2 == 0) memcpy(pBuf,"g",1); else memcpy(pBuf,"u",1); cnt++; rc = send(pPerHandleData->Socket,pBuf,8,0); delete []pBuf; closesocket(pPerHandleData->Socket); } break; case GdOpUpdateTask: { if (!g_bFrameOnline) { g_bFrameOnline = true; StartDoNetControlRVC(); } } break; case GdOpQueryInstall: { if (!g_bFrameOnline) { g_bFrameOnline = true; StartDoNetControlRVC(); } int rc,err; char* pBuf = new char[8]; memset(pBuf,0,8); if (cnt%2 == 0) memcpy(pBuf,"y",1); else memcpy(pBuf,"n",1); cnt++; rc = send(pPerHandleData->Socket,pBuf,8,0); delete []pBuf; closesocket(pPerHandleData->Socket); } break; case GdOpUpgradeRestart: { if (pInfo->dwParam1 == 6)//need Upgrade guardian !!!Only!!! { g_needUpgradeGuardianOnly = true; } if (pInfo->dwParam1 == 4)//just know framework is starting... { LOG4VTM(INFO, "framework is starting..."); //oilyang@20211208 add the following if receive framework is starting ,reset g_dwUpgradeRestartTimeBegin //in order to skip the cost time of "kill spshell" g_dwUpgradeRestartTimeBegin = GetTickCount64(); g_bFrameOnline = true; g_bAuthSuc = false; StartDoNetControlRVC(); break; } if (pInfo->dwParam1 == 5) { LOG4VTM(INFO, "framework auth ok."); g_bInUpgrade = false; g_bAuthSuc = true; } if (!g_bFrameOnline) { g_bFrameOnline = true; StartDoNetControlRVC(); } if (pInfo->dwParam1 == 1 || pInfo->dwParam1 == 3) g_dwTimeBegin = GetTickCount64(); ServerReportEvent("upgrade restart"); LOG4VTM(INFO, "upgrade restart " << pInfo->dwParam1); if (pInfo->dwParam1 == 1)//framework to restart after upgrade { g_bInUpgrade = true; WriteRunInfoContent("111"); g_dwUpgradeRestartTimeBegin = GetTickCount64(); } else if (pInfo->dwParam1 == 2)//need rollback after upgrade { LOG4VTM(INFO, "upgrade restart 222"); WriteRunInfoContent("222"); //rollback EnterCriticalSectionRVC(g_cs); g_needToRollBack = 0; LeaveCriticalSectionRVC(g_cs); LOG4VTM(INFO, "healthmanager said to rollback"); FrameworkRollBack(); }else if (pInfo->dwParam1 == 3)//upgrade succeeded { LOG4VTM(INFO, "upgrade restart ok"); WriteRunInfoContent("333"); EnterCriticalSectionRVC(g_cs); g_needToRollBack = 0; LeaveCriticalSectionRVC(g_cs); } //runinfo.close(); closesocket(pPerHandleData->Socket); } break; case GdOpFrameQuit: if (!g_bFrameOnline) { g_bFrameOnline = true; StartDoNetControlRVC(); } EnterCriticalSectionRVC(g_cs); g_bFrameQuit = true; LeaveCriticalSectionRVC(g_cs); closesocket(pPerHandleData->Socket); break; default: break; } } DWORD WINAPI ProcessIO(LPVOID lpParam) { HANDLE CompletionPort = (HANDLE)lpParam; DWORD BytesTransferred; LPPER_HANDLE_DATA PerHandleData; LPPER_IO_OPERATION_DATA PerIoData; while(true) { if(0 == GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (LPDWORD)&PerHandleData, (LPOVERLAPPED*)&PerIoData, INFINITE)) { if( (GetLastError() == WAIT_TIMEOUT) || (GetLastError() == ERROR_NETNAME_DELETED) ) { LOG4VTM(INFO, "closing socket(1) " << PerHandleData->Socket); closesocket(PerHandleData->Socket); delete PerIoData; delete PerHandleData; continue; } else { //OutErr("GetQueuedCompletionStatus failed!"); } return 0; } // client quit if(BytesTransferred == 0) { LOG4VTM(INFO, "closing socket(2) " <Socket); closesocket(PerHandleData->Socket); delete PerIoData; delete PerHandleData; continue; } // receiving data process DataProcess(PerHandleData,PerIoData); // socket WSARecv DWORD Flags = 0; DWORD dwRecv = 0; ZeroMemory(PerIoData, sizeof(PER_IO_OPERATION_DATA)); PerIoData->DataBuf.buf = PerIoData->Buffer; PerIoData->DataBuf.len = DATA_BUFSIZE; WSARecv(PerHandleData->Socket, &PerIoData->DataBuf, 1, &dwRecv, &Flags, &PerIoData->Overlapped, NULL); } return 0; } DWORD WINAPI DoWork(void* pData) { LOG4VTM(INFO, "to wait."); HANDLE hTimer; LARGE_INTEGER li; hTimer = CreateWaitableTimer(NULL,FALSE,NULL); const int nTimerUnitsPerSecond = 10000000; li.QuadPart = -(1*60*nTimerUnitsPerSecond);//oiltmp 1 minute SetWaitableTimer(hTimer,&li,2*60*1000,NULL,NULL,FALSE); while(1) { WaitForSingleObject(hTimer,INFINITE); EnterCriticalSectionRVC(g_cs); if (!g_bFrameQuit) { ULONGLONG dwTmpBegin = g_dwTimeBegin; ULONGLONG dwUpgradeTmpBegin = g_dwUpgradeRestartTimeBegin; LeaveCriticalSectionRVC(g_cs); ULONGLONG dwTimeEnd = GetTickCount64(); //oilyang@20190828 add //升级重启后,在10分钟内,只要离最后一次交互时间大于MAX_WAIT_TIME_TO_RESTART_UPGRADE,重启框架 if (g_bInUpgrade && ((dwTimeEnd - dwTmpBegin) < MAX_WAIT_TIME_TO_RESTART) && (!g_bAuthSuc && ((dwTimeEnd - dwUpgradeTmpBegin) > MAX_WAIT_TIME_TO_RESTART_UPGRADE))) { LOG4VTM(WARN, "**in upgrade restart**,to restart framework."); FrameworkShutdown(true,true); } if ((dwTimeEnd-dwTmpBegin) > MAX_WAIT_TIME_TO_RESTART) { LOG4VTM(ERROR, "framework maybe down."); string strRunInfo; if (ReadRuninfoContent(strRunInfo)) { LOG4VTM(INFO, "to check if need rollback."); LOG4VTM(INFO, "runinfo:" << strRunInfo); if (strRunInfo.compare("111") == 0 && IsStartTimeFileExist()) { EnterCriticalSectionRVC(g_cs); g_needToRollBack = 1; LeaveCriticalSectionRVC(g_cs); } } else LOG4VTM(WARN, "read run info failed."); if (g_needToRollBack) { LOG4VTM(INFO, "after upgrade,time elapse,but can't wait the shake hands,so rollback."); FrameworkRollBack(); } else { //framework is down,to restart it! oilyang 20150413 //oilyang@20211221 if Upgrade guardian !!!Only!!! ,just reset dwTmpBegin if (!g_needUpgradeGuardianOnly) FrameworkShutdown(); else dwTmpBegin = GetTickCount64(); } } } else LeaveCriticalSectionRVC(g_cs); } EndThreadRVC(); return 0; } DWORD WINAPI DoNetControl(LPVOID pData) { ServerReportEvent("DoNetControl start"); HANDLE hSnapshot; //find spshell.exe hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnapshot) { PROCESSENTRY32 pe; pe.dwSize = sizeof(pe); if (Process32First(hSnapshot, &pe)) { do { if (_stricmp(&pe.szExeFile[0], "spshell.exe") == 0) { //ServerReportEvent("find spshell.exe"); LOG4VTM(INFO, "find spshell.exe"); //DWORD dwExit = 0; //do //{ // GetExitCodeProcess(&pe.th32ProcessID,&dwExit); // if (dwExit != STILL_ACTIVE) // break; //}while(1); HANDLE hP = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID ); WaitForSingleObject(hP, INFINITE ); LOG4VTM(INFO, "spshell.exe quit"); break; } } while (Process32Next(hSnapshot, &pe)); } CloseHandle(hSnapshot); } else ServerReportEvent("create snapshot failed."); EndThreadRVC(); return 0; } #endif //linux int InitListenSocketRVC() { #ifdef linux socklen_t clilen; epfd = epoll_create(256); struct sockaddr_in clientaddr; struct sockaddr_in serveraddr; g_sListen = socket(AF_INET, SOCK_STREAM, 0); //setnonblocking(listenfd); ev.data.fd = g_sListen; ev.events = EPOLLIN | EPOLLET; epoll_ctl(epfd, EPOLL_CTL_ADD, g_sListen, &ev); memset(&serveraddr, 0, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; char* local_addr = "127.0.0.1"; inet_aton(local_addr, &(serveraddr.sin_addr)); serveraddr.sin_port = htons(default_port); int tmpuse = 1; if (setsockopt(g_sListen, SOL_SOCKET, SO_REUSEPORT, (char*)&tmpuse, sizeof(tmpuse)) < 0) { LOG4VTM(ERROR, "setsockopt failed. "<< errno); LOG4VTM(ERROR, strerror(errno)); close(g_sListen); return -1; } int ret = bind(g_sListen, (sockaddr*)& serveraddr, sizeof(serveraddr)); if (ret != 0) { LOG4VTM(ERROR, "bind failed: " << errno); LOG4VTM(ERROR, strerror(errno)); close(g_sListen); return -1; } ret= listen(g_sListen, LISTENQ); if (ret != 0) { LOG4VTM(ERROR, "listen failed: " << errno); LOG4VTM(ERROR, strerror(errno)); close(g_sListen); return -1; } LOG4VTM(INFO, "(linux)listen ok: " << g_sListen); cout << "listen on:" << g_sListen << endl; return g_sListen; #else WSADATA wsaData; int iResult; struct addrinfo* result = NULL; struct addrinfo hints; int iSendResult; char recvbuf[DEFAULT_BUFLEN]; int recvbuflen = DEFAULT_BUFLEN; // Initialize Winsock iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); if (iResult != 0) { LOG4VTM(ERROR, "WSAStartup failed with error:" << iResult); return 1; } ZeroMemory(&hints, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; hints.ai_flags = AI_PASSIVE; // Resolve the server address and port iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result); if (iResult != 0) { LOG4VTM(ERROR, "getaddrinfo failed with error:" << iResult); WSACleanup(); return 1; } // get system info to create work thread SYSTEM_INFO SystemInfo; GetSystemInfo(&SystemInfo); // Create a SOCKET for connecting to server g_sListen = socket(result->ai_family, result->ai_socktype, result->ai_protocol); if (g_sListen == INVALID_SOCKET) { LOG4VTM(ERROR, "socket failed with error:" << WSAGetLastError()); freeaddrinfo(result); WSACleanup(); return 1; } // Setup the TCP listening socket struct sockaddr_in* sockaddr_ipv4 = (struct sockaddr_in*) result->ai_addr; LOG4VTM(INFO, "ip:"<< inet_ntoa(sockaddr_ipv4->sin_addr)); iResult = bind(g_sListen, result->ai_addr, (int)result->ai_addrlen); if (iResult == SOCKET_ERROR) { LOG4VTM(ERROR, "bind failed with error:" << WSAGetLastError()); freeaddrinfo(result); closesocket(g_sListen); WSACleanup(); return 1; } freeaddrinfo(result); LOG4VTM(INFO, "to listen."); iResult = listen(g_sListen, SOMAXCONN); if (iResult == SOCKET_ERROR) { LOG4VTM(ERROR, "listen failed with error: " << WSAGetLastError()); closesocket(g_sListen); WSACleanup(); return 1; } LOG4VTM(INFO, "listen ok."); return g_sListen; #endif //linux } void AcceptReqRVC() { #ifdef linux StartDoWorkRVC(); LOG4VTM(DEBUG, "AcceptReqRVC:after StartDoWorkRVC"); int maxi, connfd, sockfd, nfds; struct epoll_event events[20]; struct sockaddr_in clientaddr; socklen_t clilen = sizeof(struct sockaddr); ssize_t n; maxi = 0; char line[MAXLINE]; for (; ; ) { //等待epoll事件的发生 nfds = epoll_wait(epfd, events, 20, 500); //处理所发生的所有事件 for (int i = 0; i < nfds; ++i) { LOG4VTM(DEBUG, "nfds: " << nfds); if (events[i].data.fd == g_sListen)//new connection { connfd = accept(g_sListen, (sockaddr*)& clientaddr, &clilen); if (connfd < 0) { LOG4VTM(ERROR, "connfd<0"); perror("connfd<0"); exit(1); } //setnonblocking(connfd); char* str = inet_ntoa(clientaddr.sin_addr); LOG4VTM(DEBUG, "accapt a connection from " << str); ev.data.fd = connfd; ev.events = EPOLLIN | EPOLLET; //ev.events=EPOLLIN; epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev); } else if (events[i].events & EPOLLIN) { LOG4VTM(DEBUG, "EPOLLIN,i:" << i << ",sock:" << events[i].data.fd); if ((sockfd = events[i].data.fd) < 0) continue; if ((n = read(sockfd, line, MAXLINE)) < 0) { if (errno == ECONNRESET) { close(sockfd); events[i].data.fd = -1; } else LOG4VTM(ERROR, "readline error: " << strerror(errno)); } else if (n == 0) { close(sockfd); events[i].data.fd = -1; } DataProcessLinux(sockfd,line); line[n] = '\0'; ev.data.fd = sockfd; ev.events = EPOLLOUT | EPOLLET; epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); LOG4VTM(DEBUG, "out DataProcessLinux"); } else if (events[i].events & EPOLLOUT) { LOG4VTM(DEBUG, "epoll out"); sockfd = events[i].data.fd; write(sockfd, line, n); ev.data.fd = sockfd; ev.events = EPOLLIN | EPOLLET; epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev); } } } #else HANDLE hCompletionPort; hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); // get system info to create work thread SYSTEM_INFO SystemInfo; GetSystemInfo(&SystemInfo); for (int i = 0; i < SystemInfo.dwNumberOfProcessors * 2; i++) { HANDLE hProcessIO = CreateThread(NULL, 0, ProcessIO, hCompletionPort, 0, NULL); if (hProcessIO) CloseHandle(hProcessIO); } //waiting thread StartDoWorkRVC(); SOCKET sClient; LPPER_HANDLE_DATA PerHandleData; LPPER_IO_OPERATION_DATA PerIoData; while (true) { //sClient = WSAAccept(sListen, NULL, NULL, NULL, 0); sClient = accept(g_sListen, NULL, NULL); //cout << "Socket " << sClient << "connect" << endl; PerHandleData = new PER_HANDLE_DATA(); PerHandleData->Socket = sClient; // client completion port CreateIoCompletionPort((HANDLE)sClient, hCompletionPort, (DWORD)PerHandleData, 0); // PerIoData = new PER_IO_OPERATION_DATA(); ZeroMemory(PerIoData, sizeof(PER_IO_OPERATION_DATA)); PerIoData->DataBuf.buf = PerIoData->Buffer; PerIoData->DataBuf.len = DATA_BUFSIZE; // WSARecv DWORD Flags = 0; DWORD dwRecv = 0; WSARecv(sClient, &PerIoData->DataBuf, 1, &dwRecv, &Flags, &PerIoData->Overlapped, NULL); LOG4VTM(DEBUG, "hold"); } DWORD dwByteTrans; PostQueuedCompletionStatus(hCompletionPort, dwByteTrans, 0, 0); closesocket(g_sListen); #endif //linux } #if linux static void sig_handle(int signo) { switch (signo) { case SIGSEGV: { LOG4VTM(INFO, "=========>>> capture signal SIGSEGV <<<========="); break; } case SIGTERM: LOG4VTM(INFO, "=========>>> capture signal SIGTERM <<<========="); break; case SIGCHLD: { LOG4VTM(INFO, "=========>>> capture signal SIGCHLD <<<========="); if (signal(SIGCHLD, sig_handle) == SIG_ERR) LOG4VTM(ERROR, "signal error."); int status; pid_t pid; while ((pid = waitpid(0, &status, WNOHANG)) > 0) { char szMsg[256] = { '\0' }; bool excep(false); if (WIFEXITED(status)) { sprintf(szMsg, "child process %d terminated normal with exit code: %d", pid, WEXITSTATUS(status)); } else { const int signum = WTERMSIG(status); sprintf(szMsg, "child process %d has been terminated unexpectly by signal %d", pid, signum); //if(signum != SIGKILL) excep = true; } LOG4VTM(INFO, szMsg); if (g_SpShellPID != 0 && g_SpShellPID == pid) { LOG4VTM(INFO, "Specified spshell process has exited!"); g_SpShellPID = 0; if (excep) { LOG4VTM(INFO, "Confirm other relate process has been terminated."); int count = 60; alive_process_info processes[60]; memset(processes, 0, sizeof(processes)); if (!osutil_detect_unique_app(relate_processes_ex, array_size(relate_processes_ex), &count, processes)) { bool spshell_exist(false); for (int i = 0; i < count; ++i) { if (strcmp(processes[i].name, spshell_execute_name) == 0) { spshell_exist = true; break; } } for (int i = 0; !spshell_exist && i < count; ++i) { kill(processes[i].pid, SIGKILL); LOG4VTM(INFO, "kill " << processes[i].name << ", pid: " << processes[i].pid << ", err: " <>> capture signal %d <<<=========", signo); LOG4VTM(INFO, szMsg); break; } return; } #endif int main(int argc, char* argv[]) { //attach file descriptors 0,1,2 to /dev/null(for deamon app) std::string terminalno; int fd0, fd1, fd2; g_bFrameOnline = true; //MessageBox(NULL, NULL, NULL, 0); if (argc > 1) terminalno = argv[1]; #ifdef linux fd0 = open("/dev/null", O_RDWR); fd1 = dup(0); fd2 = dup(0); pthread_mutex_init(&g_cs_event,NULL); pthread_mutex_init(&g_cs_log,NULL); pthread_mutex_init(&g_cs,NULL); #else InitializeCriticalSectionAndSpinCount(&g_cs_event, 100); InitializeCriticalSectionAndSpinCount(&g_cs_log, 100); InitializeCriticalSectionAndSpinCount(&g_cs, 100); ZeroMemory(currDirBuf, sizeof(currDirBuf)); ZeroMemory(chDisk, sizeof(chDisk)); GetCurrentDirectory(256, currDirBuf); chDisk[0] = currDirBuf[0]; #endif cmb::log_init_config config; config.dev_name = "guardian"; config.terminalno = terminalno; ///*TODO: 依赖上层提供环境变量来控制路径和开关,可以考虑厂商的方式 (80374374@1/4/2024)*/ #if defined(_MSC_VER) config.log_dir = ("C:\\rvc\\dbg\\"); #else config.log_dir = ("/opt/rvc/dbg/"); #endif //_MSC_VER std::string str; cmb::log4rvcother::init(config, str); g_dwTimeBegin = GetTickCount64(); int i=1000; LOG4VTM(INFO, "guardian version: " << GUARDIAN_VERSION_STR); #if linux g_SpShellPID = GetSpshellProcID(); LOG4VTM(INFO, ("get spshell proc id: ") << g_SpShellPID); if (signal(SIGCHLD, sig_handle) == SIG_ERR) { LOG4VTM(WARN, ("register for SIGCHLD failed: ") << errno); } else { LOG4VTM(DEBUG, ("register for SIGCHLD succ.")); } #endif StartDoNetControlRVC(); InitListenSocketRVC(); AcceptReqRVC(); }