#if (defined _WIN32 || defined _WIN64) #include "stdafx.h" #include #include #endif #include "baseEx.h" #include "CWebsocketServer.h" #include "mod_chromium.h" #include "base64.h" #include "CModTools.h" #include "cJSON.h" #include "SpIni.h" #include "CSocketClient.h" #include "processControl.h" #include #include #include "path.h" #include "guitask/guitask.h" #include "EventCode.h" #include "../mod_upload/Upload_client_g.h" #define COMPKEY_TERMINATE ((UINT_PTR) 0) #define COMPKEY_STATUS ((UINT_PTR) 1) #define COMPKEY_JOBOBJECT ((UINT_PTR) 2) #define LOG_EVT_SELFCHECK_ASSISTANTCHANNEL_IDLE 0x50500001 //协助通道重启 //#define EVENT_UKEY_PULLOUT 0x21000008 // 证书拔出 0x21000008 1 检测到维护证书拔出,用于驱动本地维护状态 #define LOG_EVT_BEGIN_CAMERA_CONFIG 0x21D00001 #define LOG_EVT_END_CAMERA_CONFIG 0x21D00002 #define MSG_EVT_STARTACTIVECAPTURE 0x31400001 //开始主动活体 #define MSG_EVT_STOPACTIVECAPTURE 0x31400002 //停止主动活体 #define LOG_EVT_SHOWACTIVECAPTUREMSG 0x31400003 //显示主动活体提示消息 #define CLIENT_MANAGER_PATH "c:\\Program Files\\CmbWinPad\\CmbWinPad.exe" namespace Chromium { CChromiumEntity::CChromiumEntity() :m_pWsServer(NULL), m_iTcpBridgePort(4504), m_pTimerListener(NULL), m_strCustomMainUrl(true) { DbgEx("CChromiumEntity constructor"); boost::thread(boost::bind(&CChromiumEntity::CefClintNotify, this)).detach(); //后台自动运行 } CChromiumEntity::~CChromiumEntity() { if (NULL != m_pWsServer) { free(m_pWsServer); m_pWsServer = NULL; } //DeleteCriticalSection(&g_csInvokFreeRDP); } #if (defined _WIN32 || defined _WIN64) void CChromiumEntity::JobNotify() { TCHAR sz[2000] = _T(""); BOOL fDone = FALSE; while (!fDone) { DWORD dwBytesXferred; ULONG_PTR CompKey; LPOVERLAPPED po; GetQueuedCompletionStatus(m_hIOCP, &dwBytesXferred, &CompKey, &po, INFINITE); fDone = (CompKey == COMPKEY_TERMINATE); if (CompKey == COMPKEY_JOBOBJECT) { switch (dwBytesXferred) { case JOB_OBJECT_MSG_END_OF_JOB_TIME: Dbg("Job time limit reached"); break; case JOB_OBJECT_MSG_END_OF_PROCESS_TIME: Dbg("Job process (Id=%d) time limit reached", po); break; case JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT: Dbg("Too many active processes in job"); break; case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO: Dbg("Job contains no active processes"); break; case JOB_OBJECT_MSG_NEW_PROCESS: Dbg("New process (Id=%d) in Job", po); break; case JOB_OBJECT_MSG_EXIT_PROCESS: Dbg("Process (Id=%d) terminated", po); break; case JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS: Dbg("Process (Id=%d) terminated abnormally", po); break; case JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT: Dbg("Process (Id=%d) exceeded memory limit", po); break; case JOB_OBJECT_MSG_JOB_MEMORY_LIMIT: Dbg("Process (Id=%d) exceeded job memory limit", po); break; default: Dbg("Unknown notification: %d", dwBytesXferred); break; } CompKey = 1; } } } #endif void CChromiumEntity::OnPreStart(CAutoArray strArgs, CSmartPointer pTransactionContext) { GetFunction()->GetSystemStaticInfo(m_sysInfo); ErrorCodeEnum Error; Error = GetFunction()->StartTcpBridgeServer(m_iTcpBridgePort); #if (defined _WIN32 || defined _WIN64) Error = GetFunction()->SubscribeBroadcast("IEBrowser", "CustomerCmd", this, m_uidBrowserListenser); if (Error != Error_Succeed) { LOG_TRACE("subscribe browser CustomerCmd failed!"); pTransactionContext->SendAnswer(Error); } else LOG_TRACE("subscribe browser CustomerCmd success!"); #endif if (Error != Error_Succeed) { LOG_TRACE("start tcp bridge server failed!"); pTransactionContext->SendAnswer(Error); return; } const int ArgsCount = strArgs.GetCount(); LOG_TRACE("strArgs.GetCount() = %d", ArgsCount); for (int i = 0; i < ArgsCount; ++i) { Dbg("strArgs[%d] = %s", i, strArgs[i].GetData()); } if(ArgsCount == 1) { m_strCustomMainUrl = strArgs[0]; Dbg("Get custom main ulr: %s", m_strCustomMainUrl.GetData()); } CModTools::get_mutable_instance().InitCModTools(this); CModTools::get_mutable_instance().killAllChromium(); // load all struct define xml & start websocket server CSimpleStringA strStructPath; GetFunction()->GetPath("Base", strStructPath); strStructPath.Append(CSimpleStringA(SPLIT_SLASH_STR) + "res" + SPLIT_SLASH_STR + "StructConfig" + SPLIT_SLASH_STR); DbgEx("find struct config files in path %s", strStructPath.GetData()); m_pWsServer = new CWebsocketServer(strStructPath, this); m_pWsServer->run(); try { chromiumRpcTask::get_mutable_instance().openrpcServer(DEFAULT_CHROMIUM_RPCPORT); DbgEx("start rpcTask in port %d", DEFAULT_CHROMIUM_RPCPORT); } catch (...) { DbgEx("open rpcTask failed"); } /*低柜 //LOG_EVT_BEGIN_CAMERA_CONFIG, LOG_EVT_END_CAMERA_CONFIG Error = GetFunction()->SubscribeLog(m_uidCameraListener, this, Log_Event, Severity_None, Error_IgnoreAll, -1, "CameraConfigManage"); if (Error != Error_Succeed) LOG_TRACE("subscribe Camera log failed!"); //非必须 */ Error = GetFunction()->SubscribeLog(m_uuidAccessAuth, this, Log_Event, Severity_None, Error_IgnoreAll, -1, "AccessAuthorization"); if (Error != Error_Succeed) LOG_TRACE("subscribe AccessAuthorization log failed!"); else LOG_TRACE("subscribe AccessAuthorization success!"); GetFunction()->SubscribeLog(m_uuidAccessAuthErr, this, Log_Error, Severity_None, Error_IgnoreAll, -1, "AccessAuthorization"); GetFunction()->RegistSysVarEvent("UIState", this); GetFunction()->RegistSysVarEvent("EntryPermit", this); generateBussinessLimitTimer(); // 按照单屏方式 pTransactionContext->SendAnswer(Error_Succeed); } void CChromiumEntity::OnSysVarEvent(const char* pszKey, const char* pszValue, const char* pszOldValue, const char* pszEntityName) { Dbg("OnSysVarEvent %s, old->new:%s->%s", pszKey, pszOldValue, pszValue); if ((strnicmp(pszKey, "UIState", strlen("UIState")) == 0)) { if (strnicmp(pszValue, "M", strlen("M")) == 0) { static bool firstEnter = true; if (firstEnter) { firstEnter = false; Dbg("first Enter main page"); } } } } void CChromiumEntity::OnPreClose(EntityCloseCauseEnum eCloseCause, CSmartPointer pTransactionContext) { if (m_pTimerListener != NULL) { GetFunction()->KillTimer(CHROMIUM_TIMER_ID); delete m_pTimerListener; m_pTimerListener = NULL; } GetFunction()->UnsubscribeBroadcast("IEBrowser"); pTransactionContext->SendAnswer(Error_Succeed); } void CChromiumEntity::OnLog(const CAutoArray& SubIDs, const CUUID nLogID, const LogTypeEnum eLogType, const SeverityLevelEnum eLevel, const DWORD dwSysError, const DWORD dwUserCode, const DWORD dwEntityInstanceID, const WORD wEntityDevelID, const CAutoArray& Param, const char* pszEntityName, const char* pszModuleName, const char* pszMessage) { Dbg("OnLog %x", dwUserCode); switch (dwUserCode) { case LOG_EVT_BEGIN_CAMERA_CONFIG://no use { auto openRet = CModTools::get_mutable_instance().StartChromiumBrowser(ERR_PAGE_REASON::CameraConfig); Dbg("show Screen Camera config, open page cameraconfig, %d", openRet.first); } break; case LOG_EVT_END_CAMERA_CONFIG://no use Dbg("end show Screen Camera config, close page cameraconfig"); CModTools::get_mutable_instance().killChromiumByName((+PAGE_TYPE::CameraConfig)._to_string()); break; case EVENT_ACCESSAUTH_SUCCEED: Dbg("access auth success, open page mainurl"); { CSystemRunInfo sysruninfo; GetFunction()->GetSystemRunInfo(sysruninfo); if (!(sysruninfo.dwBootOption & SystemBootOptionEnum::BootOption_Test)) generateCefclientTimer();//非--test时,才会打开浏览器 } break; case EVENT_ACCESSAUTH_FAILED: case EVENT_ACCESSAUTH_TIMEOUT: { auto openRet = CModTools::get_mutable_instance().StartChromiumBrowser(ERR_PAGE_REASON::breakdown); Dbg("access failed, open page breakdown, %d", openRet.first); } break; default: break; } } void CChromiumEntity::OnTerminalManage(const char* pszEntityName, DWORD dwMessageId, DWORD dwMessageSignature, HealthManager::TerminalManager& evt) { Dbg("OnTerminalManage : evt.op = %d", evt.op); switch (evt.op) { case 0: GetFunction()->SetSysVar("TerminalManagerState", "L", true); { auto openRet = CModTools::get_mutable_instance().StartChromiumBrowser(ERR_PAGE_REASON::TerminalManagerOff); Dbg("TerminalManagerState L, open page TerminalManagerOff, %d", openRet.first); } break; case 1: GetFunction()->SetSysVar("TerminalManagerState", "N", true); { Dbg("TerminalManagerState N, try close TerminalManager"); CModTools::get_mutable_instance().killChromiumByName((+PAGE_TYPE::TerminalManager)._to_string()); } break; case 2: GetFunction()->SetSysVar("TerminalManagerState", "K", true); { auto openRet = CModTools::get_mutable_instance().StartChromiumBrowser(ERR_PAGE_REASON::TerminalManagerKickOut); Dbg("TerminalManagerState K, open page TerminalManagerKickOut, %d", openRet.first); } break; case 99: GetFunction()->SetSysVar("TerminalManagerState", "N", true); { Dbg("TerminalManagerState N, try close TerminalManager"); CModTools::get_mutable_instance().killChromiumByName((+PAGE_TYPE::TerminalManager)._to_string()); } break; default: break; } } void CChromiumEntity::OnBusinessLimitTimerListener(void* pData) { Dbg("定时任务检查业务禁用"); // 检查是否禁用业务,做交易限制的检查 TradeManageCodeEnum jobLimit = CModTools::get_mutable_instance().CheckJobLimited(); Dbg("ShowLimitScreen : TradeManageCodeEnum=%s", jobLimit._to_string()); switch (jobLimit) { case TradeManageCodeEnum::Trade: CModTools::get_mutable_instance().killChromiumByName((+PAGE_TYPE::TradeManager)._to_string()); break; case TradeManageCodeEnum::Disabled: { auto openRet = CModTools::get_mutable_instance().StartChromiumBrowser(ERR_PAGE_REASON::disabled); Dbg("TradeManageCodeEnum Disabled, open page %d", openRet.first); } break; case TradeManageCodeEnum::JobUncomplete: { auto openRet = CModTools::get_mutable_instance().StartChromiumBrowser(ERR_PAGE_REASON::jobuncomplete); Dbg("TradeManageCodeEnum JobUncomplete, open page %d", openRet.first); } break; default: break; } GetFunction()->ResetTimer(BROWSER_TIMER_ID, BROWSER_TIMER_INTERVAL); } void CChromiumEntity::CefClintNotify() { #if(defined _WIN32 || defined _WIN64) while (1) { for (auto it = m_cefArr.begin(); it != m_cefArr.end(); it++) { if (WaitForSingleObject(std::get<1>(it->second), 10) != WAIT_OBJECT_0) //process end DbgEx("cefclient.exe tag:%s job:%d, process:%d checkOpen", it->first.c_str(), std::get<0>(it->second), std::get<1>(it->second)); else { DbgEx("cefclient.exe tag:%s job:%d, process:%d check Closed, try to restart", it->first.c_str(), std::get<0>(it->second), std::get<1>(it->second)); auto info = it->first; auto cmdline = std::get<2>(it->second);//save the param TerminateJobObject(std::get<0>(it->second), 0); m_cefArr.erase(it->first); auto startRet = startProcessInJob(cmdline, ""); if (std::get<0>(startRet)) m_cefArr.insert(std::make_pair(info, std::make_tuple(std::get<1>(startRet), std::get<2>(startRet), cmdline))); break; } } boost::this_thread::sleep_for(boost::chrono::seconds(10)); } #endif } #if (defined _WIN32 || defined _WIN64) void CChromiumEntity::OnCustomerCmd(const char* pszEntityName, DWORD dwMessageId, DWORD dwMessageSignature, IEBrowser::CustomerCmd& evt) { DbgEx("OnCustomerCmd %s", evt.cmdStr); static std::pair historyChromium; auto jsDeletor = [](cJSON* p) { cJSON_Delete(p); }; std::unique_ptr cmdJs(cJSON_Parse(evt.cmdStr), jsDeletor); auto cmdStr = cJSON_GetObjectItem(cmdJs.get(), "command")->valuestring; auto chromiumCLoseInJob = [&](std::string closeJob) -> bool { auto it = m_cefArr.find(closeJob); if (m_cefArr.end() != it) { TerminateJobObject(std::get<0>(it->second), 0); m_cefArr.erase(closeJob); return true; } else { DbgEx("m_cefArr can not find %s", closeJob.c_str()); return false; } }; if (!strcmp(cmdStr, "ChromiumStart")) { auto info = cJSON_GetObjectItem(cmdJs.get(), "info")->valuestring; auto fullscreen = cJSON_GetObjectItem(cmdJs.get(), "fullscreen")->valueint; auto top = cJSON_GetObjectItem(cmdJs.get(), "top")->valueint; // 感觉应当通过FreeRDP来启动Chromium,不应该自己管理 // 需要保存启动的chromium的句柄值或者进程Id,通过该进程Id来获取句柄管理chromium运行 // 因为有多个chromium,此时对每一个H5页面,都保存连接。并无法感知到某一个chromium的连接都不存在来重启chromium chromiumCLoseInJob(info); CSmartPointer spCerConfig; GetFunction()->OpenConfig(Config_CenterSetting, spCerConfig); CSimpleString UserMgrUrlFulture; SpIniMappingTable table; // clean cache every time table.AddEntryString(GetEntityName(), "UserMgrUrlFulture", UserMgrUrlFulture, ""); if (Error_Succeed != table.Load(spCerConfig)) { DbgEx("load UserMgrUrlFulture failed"); return; } CSimpleStringA strChromiumPath; GetFunction()->GetPath("Base", strChromiumPath); strChromiumPath.Append("\\bin\\Chromium\\"); auto checkHttpThread = boost::async(boost::bind(checkHttpThreadFun, UserMgrUrlFulture.GetData())); checkHttpThread.wait_for(boost::chrono::seconds(1)); Dbg("%s http check %s", UserMgrUrlFulture.GetData(), (checkHttpThread.is_ready() && checkHttpThread.get()) ? "sucess" : "failed"); CSimpleStringA cachePath; GetFunction()->GetPath("Temp", cachePath); cachePath.Append("\\cefCache"); CSimpleStringA strCmdLine = ""; strCmdLine.Append(strChromiumPath).Append("cefclient.exe --url=").Append(UserMgrUrlFulture); strCmdLine.Append(" --hide-controls=true --cache-path=").Append(cachePath); strCmdLine.Append(" --logExtend=").Append(info).Append(" --top=").Append(std::to_string((LONGLONG)top).c_str()); if (fullscreen) strCmdLine.Append(" --fullscreen"); DbgEx("ChromiumStart cmdline : %s", strCmdLine); //control cefclient throught job auto startRet = startProcessInJob(strCmdLine.GetData(), ""); if (std::get<0>(startRet)) m_cefArr.insert(std::make_pair(info, std::make_tuple(std::get<1>(startRet), std::get<2>(startRet), strCmdLine.GetData()))); else DbgEx("startProcessInJob failed!"); } else if (!strcmp(cmdStr, "ChromiumClose")) { auto info = cJSON_GetObjectItem(cmdJs.get(), "info")->valuestring; chromiumCLoseInJob(info); } } #endif void CChromiumEntity::generateCefclientTimer() { m_pTimerListener = new TimerOutHelper(this, &CChromiumEntity::OnTaskTimerListener, NULL, false); GetFunction()->SetTimer(CHROMIUM_TIMER_ID, m_pTimerListener, 2000); //间隔执行时间 } void CChromiumEntity::generateBussinessLimitTimer() { DbgEx("Start BusinessLimitTimer"); pBusinessLimitTimerListener = new TimerOutHelper(this, &CChromiumEntity::OnBusinessLimitTimerListener, NULL, false); GetFunction()->SetTimer(BROWSER_TIMER_ID, pBusinessLimitTimerListener, 5000); } void CChromiumEntity::OnTaskTimerListener(void* pData) { static int max_restartTime = 3; DbgEx("OnTaskTimerListener"); if (max_restartTime == 0) { DbgEx("max_restartTime == 0, do not restart chromium"); GetFunction()->KillTimer(CHROMIUM_TIMER_ID); return; } //基于Desk2S功能判断,当当前并未启动cefclient时,对cefclient.exe进行清理 if (0 == m_cefArr.size()) DbgEx("kill chromium %s", CModTools::get_mutable_instance().killAllChromium() ? "success" : "fail"); auto rc = CModTools::get_mutable_instance().StartChromiumBrowser(); max_restartTime--; DbgEx("TaskTimerListen, startChromiumBrowser, rc:%d, pid:%d", rc.first, rc.second); if (0 == rc.first) GetFunction()->KillTimer(CHROMIUM_TIMER_ID); #if (defined _WIN32 || defined _WIN64) if (rc.first == Error_Succeed && rc.second != 0) { HANDLE defaultProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, rc.second); if (defaultProcess == nullptr) { DbgEx("OnTaskTimerListener, openProcess failed"); return; } auto ret = assigntoJob(defaultProcess, rc.second); if (!ret.first) DbgEx("OnTaskTimerListener, assigntoJob failed"); else { auto monitorThread = [&](HANDLE job, HANDLE process) { while (1) { if (WaitForSingleObject(process, 10) != WAIT_OBJECT_0) //process end DbgEx("default cefclient.exe job:%d, process:%d checkOpen", job, process); else { DbgEx("default cefclient.exe job:%d, process:%d check Closed, try to restart", job, process); TerminateJobObject(job, 0); max_restartTime--; generateCefclientTimer(); //modTools.StartChromiumBrowser(); break; } boost::this_thread::sleep_for(boost::chrono::seconds(10)); } }; if (ret.second != nullptr && defaultProcess != nullptr) boost::thread(monitorThread, ret.second, defaultProcess).detach(); } } if (Error_Succeed != rc.first) { DbgEx("OnTaskTimerListener will be called after %d secs, rest restart time %d", 15, max_restartTime); if (max_restartTime) GetFunction()->ResetTimer(CHROMIUM_TIMER_ID, 15000); } else { DbgEx("OnTaskTimerListener rc = succeed"); GetFunction()->KillTimer(CHROMIUM_TIMER_ID); } #endif } SP_BEGIN_ENTITY_MAP() SP_ENTITY(CChromiumEntity) SP_END_ENTITY_MAP() }